Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: stud 17. 09. 2015, 13:34:25
-
Mám třídy pro obecnou matici a čtvercovou matici. Násobení dvou obecných matic někdy dá čtvercovou matici. Lze v nějakém OO jazyce zajistit, abych v takovém případě dostal instanci čtvercové matice (aby šly použít metody dávající smysl jen pro čtvercovou matici)?
-
To přece šlo vždycky, z násobení dostanete obecný prapředek Object a zeptáte se přes nějaké instanceof, zda máte jednu nebo druhou instanci a podle toho přetypujete a pokračujete dál.
-
To přece šlo vždycky, z násobení dostanete obecný prapředek Object a zeptáte se přes nějaké instanceof, zda máte jednu nebo druhou instanci a podle toho přetypujete a pokračujete dál.
Ono jde asi o to, jak to udělat bez instanceof a přetypování.
-
Lze v nějakém OO jazyce zajistit, abych v takovém případě dostal instanci čtvercové matice (aby šly použít metody dávající smysl jen pro čtvercovou matici)?
Lze to třeba v C# - stačí zakódovat rozměry matice do jejího typu:
using Two = S<S<Z>>;
using Three = S<S<S<Z>>>;
// Prirozena cisla.
public class Nat { }
public class Z : Nat { }
public class S<N> : Nat where N : Nat { }
public class Matrix<M, N> where M : Nat where N : Nat
{
public Matrix<M, P> Multiply<P>(Matrix<N, P> other)
{
throw new NotImplementedException();
}
}
public static class SquareMatrixExtensions
{
// Pouze pro ctvercove matice.
public static int Determinant<N>(this Matrix<N, N> m)
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
var a = new Matrix<Two, Three>();
var b = new Matrix<Three, Two>();
var x = a.Multiply(b);
x.Determinant();
//b.Determinant(); // Neprelozi se.
}
}
Nicméně se obávám, že to nebude praktické. Problémy například nastanou, když rozměry matice nebudou známy v době kompilace na úrovni typového systému.
-
V C++ by se na to dal použít boost variant: http://www.boost.org/doc/libs/1_59_0/doc/html/variant.html
Ale není to moc hezké řešení a myslím si, že to ve skutečnosti nechceš.
-
To se řeší buď implicitním přetypováním za běhu (v C++, C# to nesmyslně zakazuje), nebo v jazycích umožňujících mít stejnou metodu s různým návratovým typem nějakým vesměs nepěkným trikem. Ve Swiftu by bylo například
func *(m1:Matrix,m2:Matrix)->Matrix
func *(m1:Matrix,m2:Matrix)->SquareMatrix
a když se použije (m1*m2).trace, tak se automaticky vybere druhá implementace, protože nečtvercová matice stopu nemá.
Podobně, ale poněkud elegantněji, lze řešit problém dědičnosti funktorů; například diferenciální operátor bere tenzor a vrací něco jako MultidimensionalArrayWithUpperAndLowerIndices (ať žije čitelnost :) ), nicméně některé vrací zase tenzor. V C# a C++ pak můžu mít dva delegáty/funktory, které od sebe dědí, a pokud použiju například kovariantní nebo absolutní derivaci, lze s výsledkem pracovat jako s tenzorem bez přetypování. Obecně se takové věci ale řeší těžko, protože typový systém je záležitost doby kompilace, kdežto tady se bavíme o době běhu.
-
Mám třídy pro obecnou matici a čtvercovou matici. Násobení dvou obecných matic někdy dá čtvercovou matici. Lze v nějakém OO jazyce zajistit, abych v takovém případě dostal instanci čtvercové matice (aby šly použít metody dávající smysl jen pro čtvercovou matici)?
Asi bych to nekomplikoval. Násobení vrátí obdélníkovou matici a čtvercová se z ní udělá pomocí funkce co sežere tu obdélníkovou a vykrade jí vnitřnosti (drobně si uprav podle jazyka).
Vracet různé typy jde skoro všude (třeba odvozené ze společné abstraktní matice). Akorát abys tu čtvercovou matici mohl použít, tak ji stejně musíš očekávat, nebo to testovat. A v takovém případě se nějaké rozumné přetypování ztratí.
-
A je fakt nutné mít jiný typ (třídu) pro čtvercovou matici? Jinak, podobné věci umí třeba Scala.
-
To se řeší buď implicitním přetypováním za běhu (v C++, C# to nesmyslně zakazuje) ...
Tak jednoznačně neodsuzoval. Mám pocit, že se častěji chytám do pasti, než že by mi to implicitní přetypování nějak často pomáhalo. I autoři C++ standardu si párkrát naběhli na vidle. Ta debata kam explicit patří a kam ne je celkem výživná.
-
To se řeší buď implicitním přetypováním za běhu (v C++, C# to nesmyslně zakazuje) ...
Tak jednoznačně neodsuzoval. Mám pocit, že se častěji chytám do pasti, než že by mi to implicitní přetypování nějak často pomáhalo. I autoři C++ standardu si párkrát naběhli na vidle. Ta debata kam explicit patří a kam ne je celkem výživná.
Jistě, ale zde by se právě dost hodilo.
-
To se řeší buď implicitním přetypováním za běhu (v C++, C# to nesmyslně zakazuje) ...
Tak jednoznačně neodsuzoval. Mám pocit, že se častěji chytám do pasti, než že by mi to implicitní přetypování nějak často pomáhalo. I autoři C++ standardu si párkrát naběhli na vidle. Ta debata kam explicit patří a kam ne je celkem výživná.
BTW C# v tomto případě zakazuje i explicitní casting.
-
Mám třídy pro obecnou matici a čtvercovou matici. Násobení dvou obecných matic někdy dá čtvercovou matici. Lze v nějakém OO jazyce zajistit, abych v takovém případě dostal instanci čtvercové matice (aby šly použít metody dávající smysl jen pro čtvercovou matici)?
Asi bych to nekomplikoval. Násobení vrátí obdélníkovou matici a čtvercová se z ní udělá pomocí funkce co sežere tu obdélníkovou a vykrade jí vnitřnosti (drobně si uprav podle jazyka).
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
-
A je fakt nutné mít jiný typ (třídu) pro čtvercovou matici? Jinak, podobné věci umí třeba Scala.
Je to vhodné, ale nemusí od sebe dědit. Vlastně by to mělo být struct, a tam se dědit ani nedá.
-
To přece šlo vždycky, z násobení dostanete obecný prapředek Object a zeptáte se přes nějaké instanceof, zda máte jednu nebo druhou instanci a podle toho přetypujete a pokračujete dál.
Ono jde asi o to, jak to udělat bez instanceof a přetypování.
Instanceof tam bude povinně, z důvodu že se mohou vrátit instance dvou různých nezávislých tříd a předem nevíme co se vrátí. (Teoreticky to v případě násobení matic vědět můžeme, ale tento případ se neuvažuje).
a když se použije (m1*m2).trace, tak se automaticky vybere druhá implementace, protože nečtvercová matice stopu nemá.
(m1*m2).trace nelze použít, protože dopředu nevíte co bude výsledkem operace. Když to nějakým způsobem budete umět odhadnout předem, pak samozřejmě ano.
-
To přece šlo vždycky, z násobení dostanete obecný prapředek Object a zeptáte se přes nějaké instanceof, zda máte jednu nebo druhou instanci a podle toho přetypujete a pokračujete dál.
Ono jde asi o to, jak to udělat bez instanceof a přetypování.
Instanceof tam bude povinně, z důvodu že se mohou vrátit instance dvou různých nezávislých tříd a předem nevíme co se vrátí. (Teoreticky to v případě násobení matic vědět můžeme, ale tento případ se neuvažuje).
a když se použije (m1*m2).trace, tak se automaticky vybere druhá implementace, protože nečtvercová matice stopu nemá.
(m1*m2).trace nelze použít, protože dopředu nevíte co bude výsledkem operace. Když to nějakým způsobem budete umět odhadnout předem, pak samozřejmě ano.
Lze to použít, kdyby mi to nešlo přeložit, tak to sem nepíšu. Čili jde to i bez instanceof a explicitního přetypování.
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
-
Lze to použít
Možná to lze to přeložit, ale nelze to použít v případě kdy výsledek operace nebude čtvercová matice. Pak se stane co ? Výjimka ? Runtime error ?
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
Je mi ovšem záhadou, proč to C# nepovolí.
-
Lze to použít
Možná to lze to přeložit, ale nelze to použít v případě kdy výsledek operace nebude čtvercová matice. Pak se stane co ? Výjimka ? Runtime error ?
Pochopitelně chyba za běhu, to jinak řešit nejde. Kdo explicitně použije trace, očekává čtvercovou matici. Zajímavé by bylo implementovat "inverse", protože to jde i s nečtvercovou maticí.
-
Pochopitelně chyba za běhu, to jinak řešit nejde. Kdo explicitně použije trace, očekává čtvercovou matici.
Takže musíte vědět předem, jaké třídy bude výsledek. To je samozřejmě možné přes pomocnou metodu m1.MultiplyWillProduceSquare(m2), ale je to práce navíc.
-
Pochopitelně chyba za běhu, to jinak řešit nejde. Kdo explicitně použije trace, očekává čtvercovou matici.
Takže musíte vědět předem, jaké třídy bude výsledek. To je samozřejmě možné přes pomocnou metodu m1.MultiplyWillProduceSquare(m2), ale je to práce navíc.
To je ale zbytečné a navíc ekvivalentní explicitnímu přetypování. Čili bez možnosti polymorfismu návratové hodnoty to nejde.
-
To je ale zbytečné a navíc ekvivalentní explicitnímu přetypování. Čili bez možnosti polymorfismu návratové hodnoty to nejde.
Takže zavoláte (m1*m2).trace() a když to zbuchne na runtime error, tak víte že SquareMatrix se nekoná a pokračujete kódem pro Matrix :)
-
To je ale zbytečné a navíc ekvivalentní explicitnímu přetypování. Čili bez možnosti polymorfismu návratové hodnoty to nejde.
Takže zavoláte (m1*m2).trace() a když to zbuchne na runtime error, tak víte že SquareMatrix se nekoná a pokračujete kódem pro Matrix :)
Když volám trace, tak vím, že jde o čtvercovou matici.
-
Když volám trace, tak vím, že jde o čtvercovou matici.
A prozradíte odkud to víte, že výsledek m1*m2 je čtvercová matice ?
-
To přece šlo vždycky, z násobení dostanete obecný prapředek Object a zeptáte se přes nějaké instanceof, zda máte jednu nebo druhou instanci a podle toho přetypujete a pokračujete dál.
Ono jde asi o to, jak to udělat bez instanceof a přetypování.
Ale to nejde udelat bez pretypovani! To vyplyva z jeho otazky. Chce z dvou obecnych matic dostat ctvercovou, ale jenom nekdy. Z toho plyne, ze bud:
- Zakoduje informaci o velikosti matice do typoveho systemu (pokud to vubec umoznuje).
- Pretypuje vysledek za behu.
To prvni asi nechce, jak jini napsali. Takze to musi pretypovat.
Je treba si uvedomit, ze typy v programovacich jazycich slouzi k (minimalne) trem rozdilnym ucelum. Z toho pak vyplyvaji tyto zmatky.
-
Ehm ...
ved nasobenie matic je nejaka metoda/funkcia. V nej bude najprv volany konstruktor pre vytvorenie vyslednej matice a tam moze byt volany konstruktor podla toho ci vysledkom bude stvorcova matica alebo nie.
Napr. pre python nieco take:
class ObdlzMatica():
def sucin(self, matica):
if self.stlpce == matica.riadky:
sucin = StvorMatica()
else:
sucin = ObdlzMatica()
# Kod sucinu
return sucin
class StvorMatica(ObdMatica):
def determinant(self):
pass
-
To přece šlo vždycky, z násobení dostanete obecný prapředek Object a zeptáte se přes nějaké instanceof, zda máte jednu nebo druhou instanci a podle toho přetypujete a pokračujete dál.
Ono jde asi o to, jak to udělat bez instanceof a přetypování.
Ale to nejde udelat bez pretypovani! To vyplyva z jeho otazky. Chce z dvou obecnych matic dostat ctvercovou, ale jenom nekdy. Z toho plyne, ze bud:
- Zakoduje informaci o velikosti matice do typoveho systemu (pokud to vubec umoznuje).
- Pretypuje vysledek za behu.
To prvni asi nechce, jak jini napsali. Takze to musi pretypovat.
Je treba si uvedomit, ze typy v programovacich jazycich slouzi k (minimalne) trem rozdilnym ucelum. Z toho pak vyplyvaji tyto zmatky.
Pravě že ne. Třetí cesta je polymorfismus návratové hodnoty (jde to ve Swiftu, jinak nevím, kde ještě).
-
Ehm ...
ved nasobenie matic je nejaka metoda/funkcia. V nej bude najprv volany konstruktor pre vytvorenie vyslednej matice a tam moze byt volany konstruktor podla toho ci vysledkom bude stvorcova matica alebo nie.
Napr. pre python nieco take:
class ObdlzMatica():
def sucin(self, matica):
if self.stlpce == matica.riadky:
sucin = StvorMatica()
else:
sucin = ObdlzMatica()
# Kod sucinu
return sucin
class StvorMatica(ObdMatica):
def determinant(self):
pass
V dynamickém jazyce to půjde vždy, to je jasné. Tady se řeší staticky typovaný jazyk.
-
Třetí cesta je polymorfismus návratové hodnoty (jde to ve Swiftu, jinak nevím, kde ještě).
Už jsme si ukázali že ne. Jestliže v metodě sedí trpaslík a podle své momentální nálady vyhazuje ven instanci Matrix nebo instanci SquareMatrix, tak je polymorfismus návratové hodnoty nepoužitelný.
-
Třetí cesta je polymorfismus návratové hodnoty (jde to ve Swiftu, jinak nevím, kde ještě).
Už jsme si ukázali že ne. Jestliže v metodě sedí trpaslík a podle své momentální nálady vyhazuje ven instanci Matrix nebo instanci SquareMatrix, tak je polymorfismus návratové hodnoty nepoužitelný.
Tak si to napiš (ve Swiftu). Jasně, je to náročné na pochopení, ale Swift to umí, funkční implementace je důkaz.
-
Naprasený příklad ve Scale:
package matrix {
class Matrix(array: Array[Array[Double]]) {
private val elements = array
def toArray(): Array[Array[Double]] = {
this.elements
}
def isSquareMatrix() : Boolean = {
val numRows = this.elements.length
for (subArray <- this.elements) {
if (subArray.length != numRows) return false
}
return true
}
}
class SquareMatrix(array : Array[Array[Double]]) extends Matrix(array) {
def trace() : Float = {
return 0; // dummy
}
}
}
import java.lang.RuntimeException
import matrix._
object MatrixApp extends App {
implicit def toSquareMatrix(m: Matrix) : SquareMatrix = {
if (!m.isSquareMatrix()) throw new RuntimeException("Not a square matrix!")
return new SquareMatrix(m.toArray())
}
val m = new Matrix(Array(Array(1.0, 2.0),Array(1.0, 2.0, 3.0)));
println(m.trace())
}
Implicitní ve Scale umožňuje volat metody třídy SquareMatrix ad hoc, což je lepší než drátem do oka, ale samozřejmě to znamená, že se bude znovu a znovu kontrolovat, zda na to má nárok.
Problém ale je, co s tím dál. Pro matematické problémy typu počítání s maticemi je OOP extrémně nevhodné. Matice není nic jiného než dvourozměrné pole čísel a jako taková nepotřebuje žádné metody, stačí sada funkcí, které pracují nad dvourozměrnými poli, jestli je matice čtvercová nebo ne, je jednoduchý test a stačí ho udělat jenom jednou a pak už výpočet prostě ví, že je ve větvi, kde se dá pracovat se čtvercovou maticí anebo není.
Suma sumárum, pokud to měl být příklad, byl dost nevhodný a pokud fakt autor potřebuje řešit matice, moje rada je se na celé OOP vykašlat. Nehledě na to, že knihoven pro práci s maticemi budou mraky.
-
Naprasený příklad ve Scale:
package matrix {
class Matrix(array: Array[Array[Double]]) {
private val elements = array
def toArray(): Array[Array[Double]] = {
this.elements
}
def isSquareMatrix() : Boolean = {
val numRows = this.elements.length
for (subArray <- this.elements) {
if (subArray.length != numRows) return false
}
return true
}
}
class SquareMatrix(array : Array[Array[Double]]) extends Matrix(array) {
def trace() : Float = {
return 0; // dummy
}
}
}
import java.lang.RuntimeException
import matrix._
object MatrixApp extends App {
implicit def toSquareMatrix(m: Matrix) : SquareMatrix = {
if (!m.isSquareMatrix()) throw new RuntimeException("Not a square matrix!")
return new SquareMatrix(m.toArray())
}
val m = new Matrix(Array(Array(1.0, 2.0),Array(1.0, 2.0, 3.0)));
println(m.trace())
}
Implicitní ve Scale umožňuje volat metody třídy SquareMatrix ad hoc, což je lepší než drátem do oka, ale samozřejmě to znamená, že se bude znovu a znovu kontrolovat, zda na to má nárok.
Problém ale je, co s tím dál. Pro matematické problémy typu počítání s maticemi je OOP extrémně nevhodné. Matice není nic jiného než dvourozměrné pole čísel a jako taková nepotřebuje žádné metody, stačí sada funkcí, které pracují nad dvourozměrnými poli, jestli je matice čtvercová nebo ne, je jednoduchý test a stačí ho udělat jenom jednou a pak už výpočet prostě ví, že je ve větvi, kde se dá pracovat se čtvercovou maticí anebo není.
Suma sumárum, pokud to měl být příklad, byl dost nevhodný a pokud fakt autor potřebuje řešit matice, moje rada je se na celé OOP vykašlat. Nehledě na to, že knihoven pro práci s maticemi budou mraky.
Přesně k tomu to vede, vykašlat se na OOP. Matice jsou ještě jednoduché, větší sranda je s tenzory a operacemi nad nimi. Jde to napsat objektově, ale spíš přes protokoly než dědičnost. Příklad je ostatně ve vedlejším vlákně o čtvercích a rovnoběžnících, kde je řešení přes protokoly to nejjednodušší (a nejefektivnější).
-
V dynamickém jazyce to půjde vždy, to je jasné. Tady se řeší staticky typovaný jazyk.
Tak som oprasil c++
#include <iostream>
class Matica {
protected:
int riadky;
int stlpce;
public:
Matica(int, int);
Matica* sucin(Matica *);
virtual void typ() { std::cout << "Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
class StvMatica: public Matica {
public:
StvMatica (int r, int s) : Matica (r, s) {}
virtual void typ() { std::cout << "Stvorcova Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
Matica::Matica(int r, int s)
{
this->riadky=r;
this->stlpce=s;
}
Matica* Matica::sucin(Matica *matica)
{
Matica *sucin;
if (this->stlpce == matica->riadky)
sucin = new StvMatica(matica->riadky, this->stlpce);
else
sucin = new Matica(matica->riadky, this->stlpce);
return sucin;
}
int main(void)
{
Matica *m1, *m2, *sucin;
m1 = new Matica(3, 2);
m2 = new Matica(2, 3);
sucin = m1->sucin(m2);
m1->typ();
m2->typ();
sucin->typ();
return 0;
}
Unika mi nieco preco takto nie?
PS: To "overeni" je dobry kviz o CR ;)
-
Tak si to napiš (ve Swiftu). Jasně, je to náročné na pochopení, ale Swift to umí, funkční implementace je důkaz.
Ani na třetí pokus jste nedodal vysvětlení jak by to mělo fungovat, tedy to uzavírám s tím že neumí.
Naprasený příklad ve Scale:
Neřeší zadání, to je něco úplně jiného.
-
Pravě že ne. Třetí cesta je polymorfismus návratové hodnoty (jde to ve Swiftu, jinak nevím, kde ještě).
Coz v podstate neni nic jineho nez pretypovani vysledku za behu (jak tu nekdo ukazal v Pythonu). Ale to je prave dusledek toho zmateni, o cem jsou typy.
-
Naprasený příklad ve Scale:
Neřeší zadání, to je něco úplně jiného.
Řeší problém (aby šly použít metody dávající smysl jen pro čtvercovou matici), neřeší přesnou představu (abych v takovém případě dostal instanci čtvercové matice), je ale otázka, zda zrovna na tomhle autor otázky bazíruje - doufám, že ne, protože to je podle mě docela nesmyslný požadavek. Samozřejmě, že těch možností řešení je více, třeba přes case classes, když budu chtít zůstat u Scaly a statických jazyků obecně. Dynamické jazyky, jak zde už zaznělo, nemají problém vůbec.
-
V dynamickém jazyce to půjde vždy, to je jasné. Tady se řeší staticky typovaný jazyk.
Tak som oprasil c++
#include <iostream>
class Matica {
protected:
int riadky;
int stlpce;
public:
Matica(int, int);
Matica* sucin(Matica *);
virtual void typ() { std::cout << "Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
class StvMatica: public Matica {
public:
StvMatica (int r, int s) : Matica (r, s) {}
virtual void typ() { std::cout << "Stvorcova Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
Matica::Matica(int r, int s)
{
this->riadky=r;
this->stlpce=s;
}
Matica* Matica::sucin(Matica *matica)
{
Matica *sucin;
if (this->stlpce == matica->riadky)
sucin = new StvMatica(matica->riadky, this->stlpce);
else
sucin = new Matica(matica->riadky, this->stlpce);
return sucin;
}
int main(void)
{
Matica *m1, *m2, *sucin;
m1 = new Matica(3, 2);
m2 = new Matica(2, 3);
sucin = m1->sucin(m2);
m1->typ();
m2->typ();
sucin->typ();
return 0;
}
Unika mi nieco preco takto nie?
PS: To "overeni" je dobry kviz o CR ;)
To je OK, ale nejde napsat (m1*m2).trace(), vlastně jde teď jen o to, že v některých jazycích to jde bez přetypování. V C++ ale ne...
-
Tak si to napiš (ve Swiftu). Jasně, je to náročné na pochopení, ale Swift to umí, funkční implementace je důkaz.
Ani na třetí pokus jste nedodal vysvětlení jak by to mělo fungovat, tedy to uzavírám s tím že neumí.
Naprasený příklad ve Scale:
Kód jsem psal výše, opakovat ho nebudu. Buď nevíš, co je polymorfismus, nebo návratová hodnota.
Neřeší zadání, to je něco úplně jiného.
-
Pravě že ne. Třetí cesta je polymorfismus návratové hodnoty (jde to ve Swiftu, jinak nevím, kde ještě).
Coz v podstate neni nic jineho nez pretypovani vysledku za behu (jak tu nekdo ukazal v Pythonu). Ale to je prave dusledek toho zmateni, o cem jsou typy.
Ne za běhu, ale při kompilaci. To je pravé ten rozdíl. Viz kód výše.
-
V dynamickém jazyce to půjde vždy, to je jasné. Tady se řeší staticky typovaný jazyk.
Tak som oprasil c++
#include <iostream>
class Matica {
protected:
int riadky;
int stlpce;
public:
Matica(int, int);
Matica* sucin(Matica *);
virtual void typ() { std::cout << "Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
class StvMatica: public Matica {
public:
StvMatica (int r, int s) : Matica (r, s) {}
virtual void typ() { std::cout << "Stvorcova Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
Matica::Matica(int r, int s)
{
this->riadky=r;
this->stlpce=s;
}
Matica* Matica::sucin(Matica *matica)
{
Matica *sucin;
if (this->stlpce == matica->riadky)
sucin = new StvMatica(matica->riadky, this->stlpce);
else
sucin = new Matica(matica->riadky, this->stlpce);
return sucin;
}
int main(void)
{
Matica *m1, *m2, *sucin;
m1 = new Matica(3, 2);
m2 = new Matica(2, 3);
sucin = m1->sucin(m2);
m1->typ();
m2->typ();
sucin->typ();
return 0;
}
Unika mi nieco preco takto nie?
PS: To "overeni" je dobry kviz o CR ;)
Čtverec s obdélníkem z vedlejšího vlákna vrací úder? Uvědomuješ si, že pokud někdo přidá do Matica metodu resize, nebo něco podobného, tak máš dokonalou past, že?
-
V dynamickém jazyce to půjde vždy, to je jasné. Tady se řeší staticky typovaný jazyk.
Tak som oprasil c++
#include <iostream>
class Matica {
protected:
int riadky;
int stlpce;
public:
Matica(int, int);
Matica* sucin(Matica *);
virtual void typ() { std::cout << "Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
class StvMatica: public Matica {
public:
StvMatica (int r, int s) : Matica (r, s) {}
virtual void typ() { std::cout << "Stvorcova Matica " << this->riadky << ", " << this->stlpce << "\n"; }
};
Matica::Matica(int r, int s)
{
this->riadky=r;
this->stlpce=s;
}
Matica* Matica::sucin(Matica *matica)
{
Matica *sucin;
if (this->stlpce == matica->riadky)
sucin = new StvMatica(matica->riadky, this->stlpce);
else
sucin = new Matica(matica->riadky, this->stlpce);
return sucin;
}
int main(void)
{
Matica *m1, *m2, *sucin;
m1 = new Matica(3, 2);
m2 = new Matica(2, 3);
sucin = m1->sucin(m2);
m1->typ();
m2->typ();
sucin->typ();
return 0;
}
Unika mi nieco preco takto nie?
PS: To "overeni" je dobry kviz o CR ;)
Čtverec s obdélníkem z vedlejšího vlákna vrací úder? Uvědomuješ si, že pokud někdo přidá do Matica metodu resize, nebo něco podobného, tak máš dokonalou past, že?
U násobení se dá předpokládat, že objekt je neměnný (vrací se nová matice).
-
Čtverec s obdélníkem z vedlejšího vlákna vrací úder? Uvědomuješ si, že pokud někdo přidá do Matica metodu resize, nebo něco podobného, tak máš dokonalou past, že?
Nechce sa mi lustit dalsie vlakno ani neviem ako tu past myslis. Napadlo ma taketo riesenie. Resize sa da riesit podobne ako sucin. Napisal som ako sa to podla mna da. Ako skolsky priklad to ujde, prakticky by som podobny problem asi riesil ako jednu triedu s testom na stvorec pre "stvorcove" metody. Ak naozaj musia byt stvorcove metody v samostatnej triede, tak potom asi radsej nejako metodu, ktora po teste ci to je stvorcova matica objekt pretypuje (co tu ale uz spominane na zaciatku myslim bolo).
Uz dlhsie v C++ neprogramujem, virtualne funckie som myslim nikdy v praxi vo finale nepouzil, teda aj ked mi niekedy po navrhu napadli, vzdy som myslim nasiel ine riesenie bez nich, ktore sa mi zdalo na konkretny pripad vhodnejsie.
-
Kód jsem psal výše, opakovat ho nebudu.
Pro jistotu ho tedy zopakujeme, aby bylo všem jasno že takhle to nejde:
func *(m1:Matrix,m2:Matrix)->Matrix
func *(m1:Matrix,m2:Matrix)->SquareMatrix
Řeší problém
... stejně idiotsky jako pokusit se přetypovat výsledek a čekat zda se nestane ClassCastException.
-
Kód jsem psal výše, opakovat ho nebudu.
Pro jistotu ho tedy zopakujeme, aby bylo všem jasno že takhle to nejde:
func *(m1:Matrix,m2:Matrix)->Matrix
func *(m1:Matrix,m2:Matrix)->SquareMatrix
Řeší problém
... stejně idiotsky jako pokusit se přetypovat výsledek a čekat zda se nestane ClassCastException.
Tak ať v tom mám jasno - co hlásí překladač? Pokud nic, kde je přetypování? Buď máš jinou verzi překladače, nebo jen plácáš. Nebo nevíš, co je přetypování. Doporučuju manuál ke Swiftu, sekci o inferenci typů. Jsou tam ostatně podobné příklady.
-
Tak ať v tom mám jasno - co hlásí překladač? Pokud nic, kde je přetypování? Buď máš jinou verzi překladače, nebo jen plácáš. Nebo nevíš, co je přetypování. Doporučuju manuál ke Swiftu, sekci o inferenci typů. Jsou tam ostatně podobné příklady.
Nemá to cenu, jako u blbečků na dvorečku. Jdu dělat něco jiného.
-
OK, řešení č. 2, snad už projde přes komisi:
package casematrix {
abstract class Matrix(array: Array[Array[Double]]) {
private val elements = array
def toArray(): Array[Array[Double]] = {
this.elements
}
}
object Matrix {
def newMatrix(array: Array[Array[Double]]) : Matrix = {
if (array.length == array(0).length) new SquareMatrix(array)
else new RectangleMatrix(array)
}
def multiply(first: Matrix, second: Matrix) = {
newMatrix(first.toArray()) // dummy
}
}
case class RectangleMatrix(array : Array[Array[Double]]) extends Matrix(array)
case class SquareMatrix(array : Array[Array[Double]]) extends Matrix(array) {
def trace() : Float = {
return 0; // dummy
}
}
}
import java.lang.RuntimeException
import casematrix._
object MatrixCaseApp extends App {
val m = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
val n = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
Matrix.multiply(m, n) match {
case m @ SquareMatrix(_) => println(m.trace())
case _ => println("Sorry, not a square matrix")
}
}
-
Diskuse se zase zvrtla, tak to shrňme. Jsou tři možnosti:
1. Konverze Matrix na SquareMatrix, vznikne nový objekt, který ale může využít "vnitřnosti" obecnější matice. V C++ by pak bylo něco jako SquareMatrix(a*b).trace() (násobení vždy vrací instanci Matrix).
2. Dynamické řešení, násobení vrací instanci Matrix nebo SquareMatrix podle rozměrů. Použití: ((a*b) as SquareMatrix).trace() (nic nového se nevytváří).
3. Mít dvě implementace metody pro násobení a využít inferenci typu a polymorfismus návratového typu; není nutné nic kopírovat ani použít as, prostě se napíše (a*b).trace() ("magii" udělá překladač v době kompilace). Nevýhody: jde to jen ve Swiftu (ze zmiňovaných jazyků) a ti jednodušší (programátoři-praktici, jak je někdo nazval :) ) to nejsou schopni pochopit. Je fakt, že příklad by šel asi vymyslet lepší.
(4. Mít jen jednu třídu, to ale není odpověď na otázku.)
-
Řeší problém
... stejně idiotsky jako pokusit se přetypovat výsledek a čekat zda se nestane ClassCastException.
Mohl bych se urazit, ale proč. Furt je to lepší než to, co jsi nabídl Ty (zkontrolovat třídu a pak přetypovat), protože to co nabízíš Ty, to umí samozřejmě automaticky taky. Implicitní konverze emuluje chování programu v dynamickém jazyce. Ovšem moje druhé řešení je typově bezpečné a bez trapných obezliček ve stylu instanceof.
-
OK, řešení č. 2, snad už projde přes komisi:
package casematrix {
abstract class Matrix(array: Array[Array[Double]]) {
private val elements = array
def toArray(): Array[Array[Double]] = {
this.elements
}
}
object Matrix {
def newMatrix(array: Array[Array[Double]]) : Matrix = {
if (array.length == array(0).length) new SquareMatrix(array)
else new RectangleMatrix(array)
}
def multiply(first: Matrix, second: Matrix) = {
newMatrix(first.toArray()) // dummy
}
}
case class RectangleMatrix(array : Array[Array[Double]]) extends Matrix(array)
case class SquareMatrix(array : Array[Array[Double]]) extends Matrix(array) {
def trace() : Float = {
return 0; // dummy
}
}
}
import java.lang.RuntimeException
import casematrix._
object MatrixCaseApp extends App {
val m = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
val n = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
Matrix.multiply(m, n) match {
case m @ SquareMatrix(_) => println(m.trace())
case _ => println("Sorry, not a square matrix")
}
}
Scalu detailně neznám, ale předpokládám, že lépe to v ní nepůjde.
-
Mít dvě implementace metody pro násobení a využít inferenci typu a polymorfismus návratového typu; není nutné nic kopírovat ani použít as, prostě se napíše (a*b).trace()
A jak dopadne vysledek prekladu/vyhodnoceni, pokud: a = matrix(2,3); b = matrix(3,4)
Pro uplnost dodam, ze predpokladam, ze hodnoty promennych jsou znamy az za behu programu.
-
Mít dvě implementace metody pro násobení a využít inferenci typu a polymorfismus návratového typu; není nutné nic kopírovat ani použít as, prostě se napíše (a*b).trace()
A jak dopadne vysledek prekladu/vyhodnoceni, pokud: a = matrix(2,3); b = matrix(3,4)
Pro uplnost dodam, ze predpokladam, ze hodnoty promennych jsou znamy az za behu programu.
Běh blbě, protože použitím trace říkám, že matice je čtvercová, takže někde bouchne assert.
-
Scalu detailně neznám, ale předpokládám, že lépe to v ní nepůjde.
Já v ní také nedělám, takže mi včera večer například nedocvaklo, že je zbytečné používat case class, když se dá matchnout normální třída třeba takto:
object MatrixCaseApp extends App {
val m = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
val n = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
Matrix.multiply(m, n) match {
case x: SquareMatrix => println(x.trace())
case _ => println("Sorry, not a square matrix")
}
}
To jsou ale detaily. Faktem je, že jsem si pomohl pattern matchingem, který Scala zdědila z FP větve a ne z OOP.
-
Řeší problém
... stejně idiotsky jako pokusit se přetypovat výsledek a čekat zda se nestane ClassCastException.
Mohl bych se urazit, ale proč. Furt je to lepší než to, co jsi nabídl Ty (zkontrolovat třídu a pak přetypovat), protože to co nabízíš Ty, to umí samozřejmě automaticky taky. Implicitní konverze emuluje chování programu v dynamickém jazyce. Ovšem moje druhé řešení je typově bezpečné a bez trapných obezliček ve stylu instanceof.
Proč tam je RectangleMatrix?
-
Scalu detailně neznám, ale předpokládám, že lépe to v ní nepůjde.
Já v ní také nedělám, takže mi včera večer například nedocvaklo, že je zbytečné používat case class, když se dá matchnout normální třída třeba takto:
object MatrixCaseApp extends App {
val m = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
val n = Matrix.newMatrix(Array(Array(1.0, 2.0),Array(1.0, 2.0)))
Matrix.multiply(m, n) match {
case x: SquareMatrix => println(x.trace())
case _ => println("Sorry, not a square matrix")
}
}
To jsou ale detaily. Faktem je, že jsem si pomohl pattern matchingem, který Scala zdědila z FP větve a ne z OOP.
Pattern matching je jedno z možných řešení, ale také to je jen zakuklené přetypování.
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
BTW umí nějaký (jiný) jazyk toto?
func *(x:Vector,y:Vector) -> Tensor {...}
func *(x:Vector,y:Vector) -> Double {...}
func *(x:Vector,y:Vector) -> AST {...}
let p:Double = a*b // scalar product
let p:Tensor = a*b // tensor product
let p:AST = a*b // AST
Docela praktické, hlavně ten AST.
-
Proč tam je RectangleMatrix?
V rámci zadání to nemá vliv, ale měl jsem potřebu tam dát tuhle třídu hlavně kvůli symetrii. "Nečtvercová" i čtvercová matice jsou pořád matice, pokud bych to měl brát takto, nedělal bych speciální třídu pro čtvercovou matici (jak jsem psal v prvním příspěvku v tomto tématu). Co mi ale hlavně vadí na prosté hierarchii Matrix -> SquareMatrix je skutečnost, že někdo může explicitně vytvořit instanci třídy Matrix a ta nebude umět operace čtvercové matice i přesto, že de facto zrovna čtvercová bude a to jenom proto, že někdo zkonstruoval instanci nepřesné třídy. Takhle je Matrix hezky abstraktní a tovární metoda udělá matici správné třídy hned od začátku. Samozřejmě, šlo by do konstruktorů SquareMatrix a RectangleMatrix dát i assert, ale dál jsem to rozvíjet nechtěl.
-
Pattern matching je jedno z možných řešení, ale také to je jen zakuklené přetypování.
Pattern matchingem proběhne detekce a přiznání se (s přetypováním) najednou a nemůže se stát, že zkontroluju omylem instanci na jednu třídu a pak se ji snažím přetypovat na úplně jinou s vyhozením výjimky při běhu. S pattern matchingem (typicky) nikdy nemusím používat potenciálně nebezpečnou operaci.
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
BTW umí nějaký (jiný) jazyk toto?
func *(x:Vector,y:Vector) -> Tensor {...}
func *(x:Vector,y:Vector) -> Double {...}
func *(x:Vector,y:Vector) -> AST {...}
let p:Double = a*b // scalar product
let p:Tensor = a*b // tensor product
let p:AST = a*b // AST
Docela praktické, hlavně ten AST.
V C# lze dosáhnout v podstatě téhož tak, že násobení vrátí instanci MultiplicationResult a třída MultiplicationResult bude obsahovat implicitní konverzi na double a na Tensor. AST lze získat pomocí Expression - tj. násobení místo parametru typu T bude brát parametr typu Expression<T>.
Ve Scale se použije například (ve Scale bude asi hodně možností, jak dosáhnout téhož) trik analogický tomu s CanBuildFrom - tj. násobení bude mít implicitní parametr BuildResult[T] a vracet typ T. Takto definované násobení pak může kdokoliv (i cizí kód) rozšířit na libovolný návratový typ, případně předefinovat jeho chování pro určité návratové typy. S AST lze ve Scale pracovat pomocí maker.
-
Diskuse se zase zvrtla, tak to shrňme. Jsou tři možnosti:
Vsechny tri moznosti jsou pretypovani, tam ci onde (ta treti je pretypovani po navratu z nasobeni matice). Jediny rozdil je, ze programatori treba v Pythonu chapou jeho typy jako navzajem ruzne, zatimco typovi teoretici to (nekdy) vnimaji jako jeden typ. Jak rikam, typy maji v programovani asi 3 ruzne vyznamy:
- Umoznuji statickou typovou kontrolu
- Indikuji zpusob polymorfismu
- Definuji zpusob ulozeni dat v pameti
V tom tvem tretim pripade jenom kombinujes vyznam slova "typ" 1 a 2, coz je v poradku (protoze slovo typ je zavadejici), ale neni to zadna revoluce. V Haskellu se slovo typ pouziva pro 1. vyznam, v Pythonu pro 2. vyznam, v C pro 3. vyznam (prevazne). Ve vetsine modernejsich jazyku lze kontrolovat vsechny tri vyznamy pomoci ruznych mechanismu (napr. Haskell - typove tridy umoznuji delat vyznam 2).
(I kdyz co umoznuje delat v Haskellu vyznam 3 mi neni jasne. V dobre typovanych jazycich je vyznam 3 neco jako dependency injection, prijde mi, ze je tam nejaka dualita mezi vyznamem 2 a 3 - v podstate dualita mezi typovymi tridami a daty, nebo dualita mezi interfacem a implementaci.)
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
BTW umí nějaký (jiný) jazyk toto?
func *(x:Vector,y:Vector) -> Tensor {...}
func *(x:Vector,y:Vector) -> Double {...}
func *(x:Vector,y:Vector) -> AST {...}
let p:Double = a*b // scalar product
let p:Tensor = a*b // tensor product
let p:AST = a*b // AST
Docela praktické, hlavně ten AST.
V C# lze dosáhnout v podstatě téhož tak, že násobení vrátí instanci MultiplicationResult a třída MultiplicationResult bude obsahovat implicitní konverzi na double a na Tensor. AST lze získat pomocí Expression - tj. násobení místo parametru typu T bude brát parametr typu Expression<T>.
Ve Scale se použije například (ve Scale bude asi hodně možností, jak dosáhnout téhož) trik analogický tomu s CanBuildFrom - tj. násobení bude mít implicitní parametr BuildResult[T] a vracet typ T. Takto definované násobení pak může kdokoliv (i cizí kód) rozšířit na libovolný návratový typ, případně předefinovat jeho chování pro určité návratové typy. S AST lze ve Scale pracovat pomocí maker.
Jasně, tak to umí i C++. Ale umí to ještě nějaký jazyk v době kompilace? Benchmark by asi dopadl pro C# v tomto případě špatně.
-
Diskuse se zase zvrtla, tak to shrňme. Jsou tři možnosti:
Vsechny tri moznosti jsou pretypovani, tam ci onde (ta treti je pretypovani po navratu z nasobeni matice). Jediny rozdil je, ze programatori treba v Pythonu chapou jeho typy jako navzajem ruzne, zatimco typovi teoretici to (nekdy) vnimaji jako jeden typ. Jak rikam, typy maji v programovani asi 3 ruzne vyznamy:
- Umoznuji statickou typovou kontrolu
- Indikuji zpusob polymorfismu
- Definuji zpusob ulozeni dat v pameti
V tom tvem tretim pripade jenom kombinujes vyznam slova "typ" 1 a 2, coz je v poradku (protoze slovo typ je zavadejici), ale neni to zadna revoluce. V Haskellu se slovo typ pouziva pro 1. vyznam, v Pythonu pro 2. vyznam, v C pro 3. vyznam (prevazne). Ve vetsine modernejsich jazyku lze kontrolovat vsechny tri vyznamy pomoci ruznych mechanismu (napr. Haskell - typove tridy umoznuji delat vyznam 2).
(I kdyz co umoznuje delat v Haskellu vyznam 3 mi neni jasne. V dobre typovanych jazycich je vyznam 3 neco jako dependency injection, prijde mi, ze je tam nejaka dualita mezi vyznamem 2 a 3 - v podstate dualita mezi typovymi tridami a daty, nebo dualita mezi interfacem a implementaci.)
Takže když mám
func f()->SquareMattix { ...; return SquareMatrix(...) }
tak je někde přetypování? Kde přesně? Možná jen přetypováním myslíme každý něco jiného. Pro mě je přetypování něco ve stylu
let m=Matrix(...); return SquareMatrix(matrix)
nebo
let m:Matrix=...; return m as? SquareMatrix
V prvním případě ale vzniká nový objekt a v tom druhém může vzniknout nil.
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
BTW umí nějaký (jiný) jazyk toto?
func *(x:Vector,y:Vector) -> Tensor {...}
func *(x:Vector,y:Vector) -> Double {...}
func *(x:Vector,y:Vector) -> AST {...}
let p:Double = a*b // scalar product
let p:Tensor = a*b // tensor product
let p:AST = a*b // AST
Docela praktické, hlavně ten AST.
V C# lze dosáhnout v podstatě téhož tak, že násobení vrátí instanci MultiplicationResult a třída MultiplicationResult bude obsahovat implicitní konverzi na double a na Tensor. AST lze získat pomocí Expression - tj. násobení místo parametru typu T bude brát parametr typu Expression<T>.
Ve Scale se použije například (ve Scale bude asi hodně možností, jak dosáhnout téhož) trik analogický tomu s CanBuildFrom - tj. násobení bude mít implicitní parametr BuildResult[T] a vracet typ T. Takto definované násobení pak může kdokoliv (i cizí kód) rozšířit na libovolný návratový typ, případně předefinovat jeho chování pro určité návratové typy. S AST lze ve Scale pracovat pomocí maker.
Benchmark by asi dopadl pro C# v tomto případě špatně.
Nevidím důvod, proč by to implementace C# nemohla zoptimalizovat stejně jako Swift. Nevím, jestli to RyuJIT od MS dokáže, nicméně MSIL lze přeložit i do LLVM (Mono, SharpLang, LLILC - zatím značně nekompletní) nebo do C (C# Native).
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
BTW umí nějaký (jiný) jazyk toto?
func *(x:Vector,y:Vector) -> Tensor {...}
func *(x:Vector,y:Vector) -> Double {...}
func *(x:Vector,y:Vector) -> AST {...}
let p:Double = a*b // scalar product
let p:Tensor = a*b // tensor product
let p:AST = a*b // AST
Docela praktické, hlavně ten AST.
V C# lze dosáhnout v podstatě téhož tak, že násobení vrátí instanci MultiplicationResult a třída MultiplicationResult bude obsahovat implicitní konverzi na double a na Tensor. AST lze získat pomocí Expression - tj. násobení místo parametru typu T bude brát parametr typu Expression<T>.
Ve Scale se použije například (ve Scale bude asi hodně možností, jak dosáhnout téhož) trik analogický tomu s CanBuildFrom - tj. násobení bude mít implicitní parametr BuildResult[T] a vracet typ T. Takto definované násobení pak může kdokoliv (i cizí kód) rozšířit na libovolný návratový typ, případně předefinovat jeho chování pro určité návratové typy. S AST lze ve Scale pracovat pomocí maker.
Benchmark by asi dopadl pro C# v tomto případě špatně.
Nevidím důvod, proč by to implementace C# nemohla zoptimalizovat stejně jako Swift. Nevím, jestli to RyuJIT od MS dokáže, nicméně MSIL lze přeložit i do LLVM (Mono, SharpLang, LLILC - zatím značně nekompletní) nebo do C (C# Native).
To nevím, jak C# optimalizuje. Vtip je v tom, že Swift tady nic optimalizovat nemusí, jedna metoda se vybere v době překladu. Pokud instanci MultiplicationResult v C# použiju víckrát, tak tam moc prostoru k optimalizaci není.
-
To by byl ten případ s (implicitním) přetypováním (operator SquareMatrix()), nicméně ani to mi neumožní napsat v C++ něco jako (m1*m2).trace() (na rozdíl od Swiftu).
Jo, v C++ by bylo třeba něco jako "square(m1*m2).trace()". To není zas tak zlé.
BTW umí nějaký (jiný) jazyk toto?
func *(x:Vector,y:Vector) -> Tensor {...}
func *(x:Vector,y:Vector) -> Double {...}
func *(x:Vector,y:Vector) -> AST {...}
let p:Double = a*b // scalar product
let p:Tensor = a*b // tensor product
let p:AST = a*b // AST
Docela praktické, hlavně ten AST.
V C# lze dosáhnout v podstatě téhož tak, že násobení vrátí instanci MultiplicationResult a třída MultiplicationResult bude obsahovat implicitní konverzi na double a na Tensor. AST lze získat pomocí Expression - tj. násobení místo parametru typu T bude brát parametr typu Expression<T>.
Ve Scale se použije například (ve Scale bude asi hodně možností, jak dosáhnout téhož) trik analogický tomu s CanBuildFrom - tj. násobení bude mít implicitní parametr BuildResult[T] a vracet typ T. Takto definované násobení pak může kdokoliv (i cizí kód) rozšířit na libovolný návratový typ, případně předefinovat jeho chování pro určité návratové typy. S AST lze ve Scale pracovat pomocí maker.
Benchmark by asi dopadl pro C# v tomto případě špatně.
Nevidím důvod, proč by to implementace C# nemohla zoptimalizovat stejně jako Swift. Nevím, jestli to RyuJIT od MS dokáže, nicméně MSIL lze přeložit i do LLVM (Mono, SharpLang, LLILC - zatím značně nekompletní) nebo do C (C# Native).
Pokud instanci MultiplicationResult v C# použiju víckrát, tak tam moc prostoru k optimalizaci není.
Zoptimalizuje se to tak, že MultiplicationResult se nebude vytvářet vůbec a pro každé použití implicitní konverze se data, jenž původně byla součástí MultiplicationResult, přímo předají kódu z implicitní konverze. Taková optimalizace může být například důsledkem escape analysis.
-
Takže když mám
func f()->SquareMattix { ...; return SquareMatrix(...) }
tak je někde přetypování? Kde přesně? Možná jen přetypováním myslíme každý něco jiného.
Mel bys tu funkci napsat celou, takthle to neni moc jasne. Ta funkce bere dve obecne (obdelnikove) matice a volitelne vraci ctvercovou. Tak ci onak to bude muset uzivatel pretypovat na zaklade toho, ze vysledna matice je ctvercova. Prijde mi, ze to tvoje reseni sice umozni vyjadrit, ze soucin dvou ctvercovych matic je ctvercova matice, ale ze soucin dvou obdelnikovych muze byt ctvercova uz ne.