-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #1771 - inconsistencias en list.asSet() #1786
Conversation
@nicovio en general suena bien, yo te pido que hagas un chequeo extra porque me preocupa que no se degrade la performance. Fijate este proyecto https://github.com/wollok/test-performance-set--1370 Bajátelo y corré los tests, que generan sets y lists bastante voluminosos, los métodos no deberían tardar mucho (en general < 1seg, algunos sí tardan y están ok, fijate este PR que dice más o menos lo que se esperaría, por más que solo tengas el total de lo que corrió es fácil ver si se fue al pasto o no) |
Ok lo voy a ver. Gracias Dodain! ✌️ |
@fdodino estuve corriendo los tests de Set que estan acá https://github.com/wollok/test-performance-set--1370 y compare los tiempos con los que pusiste en #1370. Algunos tardaron bastante más, así que me fije como daban antes de los cambios de este PR y las diferencias fueron las mismas. Dejo los tiempos totales que se esperaban vs los que dieron. Pruebas hechas con SetNúmerosEsperado: 4502 ms Objetos definidos por el usuario sin == ni >Esperado: 1740 ms Objetos definidos por el usuario (redefiniendo == y >)Esperado: 6692 ms StringsEsperado: 1548 ms DatesEsperado: 1036 ms BooleansEsperado: 53 ms |
Pongo acá lo que hablamos recién, puede ser una diferencia en la potencia de las máquinas, no me preocuparía por la performance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Impresionante laburo!!!
Pido disculpas por los muchos comentarios pero es un lugar delicado y ya tuvimos algunas idas y vueltas, propongo que nos tomemos la paciencia de dejarlo bien de una vez por todas.
@@ -922,13 +922,13 @@ class Set inherits Collection { | |||
} | |||
|
|||
/** | |||
* Returns a new copy of current Set. | |||
* Converts an object to a Set. No effect on Sets. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Es un poco quisquilloso pero deberíamos evitar la palabra effect
que tiene un significado bastante específico en nuestro discurso. De paso, no creo que convierta an object
to a set, vale sólo para colecciones, de hecho convierte this
.
Entonces propongo "Converts this collection into a set. If this collection is already a set, it returns a copy of it."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 a tu propuesta.
Otra cosa: Esto estaba puesto en el asSet()
de Set
, pero creo que debería estar en el de Collection
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suena lógico. @fdodino tenemos una política sobre wollokdoc en métodos que son overrides?
*/ | ||
override method asSet() native | ||
override method asSet() = self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acá cambió el comportamiento y no coincide con el comentario de arriba. Esta implementación no está bien en Collection
sólo valdría para Set
. Francamente me gustaba la idea de que el asSet
devolviera una copia, pero si prefieren evitarlo no está mal, pero deberíamos:
- mover esta implementación a
Set
- dejar este método abstracto (y chequear que otras colecciones lo implementen).
- corregir el wollokdoc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Esta implementación es la de Set
y en Collection
es abstracto.
Me parece que se genera una confusión porque esta mal la documentación y en el asSet()
de Set
dice lo que debería decir en el de Collection
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, mala mía. Igualmente me gustaba de la implementación nativa que devolvía un set nuevo en lugar de self. @fdodino @PalumboN @lspigariol qué opinan?
@@ -1402,7 +1406,7 @@ class List inherits Collection { | |||
* [1, 3, 1, 5, 1, 3, 2, 5].withoutDuplicates() => Answers [1, 2, 3, 5] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Este ejemplo está mal, está cambiando el orden y es justamente lo que no queremos.
De hecho estaría bueno agregar ese detalle en el comentario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Estoy de acuerdo con vos.
@@ -34,7 +34,7 @@ class WollokObjectComparator implements Comparator<WollokObject> { | |||
val comparator = comparisonsStrategy.get(o1.kind.fqn) ?: new WollokObjectEqualsComparator | |||
return comparator.compare(o1, o2) | |||
} catch (RuntimeException e) { | |||
return o1.hashCode.compareTo(o2.hashCode) | |||
return (o1.kind.hashCode).compareTo(o2.kind.hashCode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No me convence lo de atrapar RuntimeException
aca. ¿No podríamos estar capturando cualquier excepción inesperada y ocultándola?
Igual entiendo que este no es un problema de este PR, y de hecho esta línea me parece mejor que la anterior, al menos respeta la relación de orden que el comparator debería definir. Si quieren levantamos otro ticket para revisar esto.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahora no recuerdo por qué puse el catch, @nicovio , te acordás por qué pusiste el o1.kind.hashCode
en lugar de o1.hashCode
, ¿en qué casos salta el RuntimeException? Es lo único que me da cosita sacar. Y sí, si esto traba el PR generemos otro ticket.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
El RuntimeException salta cuando o1 y o2 son de distintos tipos.
Puse el o1.kind.hashCode
porque estaban andando mal los sets con elementos de distintos tipos, a veces aceptaba duplicados (pasaba con strings y dates).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, cierto!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O sea, o1 y o2 siempre son WollokObject, pero vos me entendiste jaja (creo).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yo entiendo dos cosas:
- Que el error que se espera atrapar es algún DNU producto de que los objetos se intentan comparar entre ellos por igualdad y si son de distinto tipo y ellos mismos no lo contemplan, posiblemente uno le termina mandando un mensaje a otro que no entiende. Ejemplo:
class Pair {
var x,y
method equals(other) = x == other.x() and y == other.y()
}
Si hacés new Pair(x=1,y=2).equals( "ola") => DNU "hola no entiende x".
A mí me gusta que se intente capturar ese caso. Capturarlo con una excepción podría ser materia de discusión, pero la alternativa es preguntar por la clase y puede tener sus propios problemas, da para una discusión larga.
El tema acá es que también estamos atrapando otros errores e ignorándolos, eso me parece más peligroso. Pero no lo discutamos acá, creo un issue para ver eso.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- La segunda es que usar el hash para el orden es peligroso porque no establece una relación de orden, ergo, podría pasar que a > b > c > a si b y c pueden compararse entre sí, pero no con a.
Entiendo que eso se evita si se usa el hash de la clase, asumiendo que si b y c son comparables, entonces son de la misma clase y entonces el kind.hash es el mismo.
Siendo estricto, esto funciona si
para todo b y c comparables (es decir,
comparator.compare(o1, o2)
no tira excepción),b.kind == c.kind
Creo que a la larga esto medio define la cuestión anterior y debemos agregar:
a. Un if o1.kind == o2.kind
antes de intentar el compare, y eso nos exime de atrapar excepciones.
b. Definir la regla "si dos objetos tienen mismo kind deben ser comparables sin tirar excepción" y documentarla.
c. De yapa, no usar kind.hashCode y en cambio comparar los nombres de los kinds entre sí. Esto es un chiche pero nos daría un ordenamiento predecible entre objetos de distinto kind. Que es un ordenamiento arbitrario (alfabético, bah) pero menos caótico que ordenar por hash y que sería consistente entre diferentes ejecuciones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Esto también obliga a tener claro cuándo es que puede pasar que dos objetos tengan el mismo kind, que asumo que no es lo mismo que tener "la misma clase", porque para eso se inventó el concepto, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No sé, si les parece podríamos meterle el fix que decía y dar por resuelto el #1791
@Test | ||
def void testListOfStringAsSetConversion() { | ||
''' | ||
assert.equals(1, ["hola","hola"].asSet().size()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me gustaría probar con dos formas de construir el mismo string, onda "ho" + "la" porque si lo construis las dos veces de la misma manera podría terminar siendo efectivamente "el mismo" y no estás testeando lo que queríamos verificar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lo mismo vale para los demás tipos de datos.
def void testListOfDictionaryAsSetConversion() { | ||
''' | ||
const dictionary = new Dictionary() | ||
assert.equals(1, [dictionary,dictionary].asSet().size()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acá efectivamente es el mismo diccionario, tenemos que probarlo creando dos dictionaries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hay un problema. Si creo dos dictionaries los considera distintos y esto [dictionary,otherDictionary].asSet().size()
da 2.
¿Hay que redefinir el equals de Dictionary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Es posible!
Igual antes de llegar a esa conclusión haría el test más unitario posible assert.equals(new Dictionary(), new Dictionary())
y vería qué pasa.
def void testListOfPairAsSetConversion() { | ||
''' | ||
const pair = new Pair(1,2) | ||
assert.equals(1, [pair,pair].asSet().size()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idem, hay que hacer el new dos veces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pasa lo mismo que con los dictionaries.
class MiClase {} | ||
program a { | ||
const miClase = new MiClase() | ||
assert.equals(1, [miClase,miClase].asSet().size()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hacer new dos veces!
Para que esto tenga sentido MiClase
debería implementar equals
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acá hay otro tema para ver.
Este test falla:
class MiClase {
override method equals(other) = return true
}
program a {
const a = new MiClase()
const b = new MiClase()
assert.equals(1, [a,b].asSet().size())
}
org.uqbar.project.wollok.tests.interpreter.WollokComparisonFailure: Expected [1] but found [2]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahora que veo todo, creo que este es el test más fácil y más representativo. Esto debería andar y si no anda es que la implementación de asSet
está mal y debemos cambiarla.
Al hacerlo, deberíamos escuchar los comentarios de @fdodino sobre las cuestiones de performance en las que estuvo trabajando, pero creo que vos ya estás al tanto de eso más que yo.
list.add(true) | ||
list.add(1) | ||
list.add("hola") | ||
assert.equals(3, list.withoutDuplicates().size()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Falta acá hacer el assert del orden.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
De acuerdo.
No me molestan tantos comentarios. Al contrario, me sirven para aprender. Yo pido disculpas si me mando muchas cagadas. |
Ahí estuvimos con los muchachos cambiando la lógica, básicamente:
Agregué 4 tests. Lo que sí pasa es que si definís esto: class Animal {
override method ==(other) = true
}
class Perro inherits Animal {
}
class Gato inherits Animal {
} cuando tenés #{ new Gato(), new Perro(), "hola" }.size() // me da 2
#{ "hola", new Gato(), new Perro() }.size() // me da 1 pero como dice @nscarcella , estamos definiendo un equals que no es conmutativo, con lo cual recontrahipersupermegaarchiexcede el alcance de este issue. |
Buenísimo, más tarde subo las correcciones del review de @npasserini. |
Tenés razón, esa implementación de equals es incorrecta, al menos en una colección que tiene cosas que no son animales. Entiendo que si restringís la colección solo a animales ahí sí es conmutativa y todo debería funcionar de acuerdo a lo esperado. Estaría bueno agregar esa regla en el comentario del Ahora... no sé cómo lo implementarías bien si no es preguntando el nombre de la clase, pero bueno, eso ya es otro tema. |
Jejeje, lo mismo dijo NicoS |
tiré kind pero es isAssignableFrom lo que necesitaríamos |
Gente, socorro. Mañana toca (en UNAHur) hablar del
Para mayor felicidad, da la clase un docente nuevo, y yo no estoy en Buenos Aires. Graciaaaas por cualquier cosa que me puedan decir. |
(mi respuesta atrasa unos cuantos mensajes). Lo unico que puedo decir para
mantener la consistencia es que en Set asSet no devuelva self sino un nuevo
set.
El lun., 16 sept. 2019 a las 11:10, Carlos Lombardi (<
notifications@github.com>) escribió:
… Gente, socorro. Mañana toca (en UNAHur) hablar del asSet() y justo el
ejemplo es con una lista de Strings.
Y auch, en el set aparece dos veces el mismo String. Imagino que es porque
no es *el mismo* String, obsérvese lo siguiente:
>>> var a = "hola"
>>> var strs = [a, "que", a, "tal"]
>>> strs.asSet()
#{"hola", "que", "tal"}
>>> var strs2 = ["hola", "que", "hola", "tal"]
>>> strs2.asSet()
#{"hola", "que", "hola", "tal"}
Para mayor felicidad, da la clase un docente nuevo, y yo no estoy en
Buenos Aires.
Yo estoy usando Wollok 1.8.5. ¿Hay alguna versión posterior en la que esto
esté arreglado?
Graciaaaas por cualquier cosa que me puedan decir.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ACZRXG3MYXL2CX2PUQE53BTQJ6HVVA5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD6ZIVBA#issuecomment-531794564>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACZRXG3BZ6UH3ZX7IM7X7S3QJ6HVVANCNFSM4IVQUROQ>
.
|
Hola @fdodino, tengo una mala noticia... parecía que estaba solucionado el problema con sets de listas y sets de sets, pero no. el problema para mi está en este método: def compareGreaterThan(WollokObject o1, WollokObject o2) {
if (o1.hasGreaterThanMethod) {
if (o1.wollokGreaterThan(o2)) 1 else -1
} else 1
} como Collection no tiene Ahí cambie los tests para que fallen. |
hola @nicovio, ok, mañana lo miro. Una cosa que yo haría por si querés probar: def compareGreaterThan(WollokObject o1, WollokObject o2) {
if (o1.hasGreaterThanMethod) {
if (o1.wollokGreaterThan(o2)) 1 else -1
} else {
// --->
if (o1.hashCode > o2.hashCode) 1 else -1
// <----
}
} |
esto no anduvo:
esto si:
|
No sé si tienen sentido ninguno de los dos. No definen relaciones de orden.
Fundamentalmente si los objetos son de distinto tipo.
El sáb., 21 de sep. de 2019 a la(s) 01:00, Nicolas Viotti (
notifications@github.com) escribió:
… if (o1.hashCode > o2.hashCode) 1 else -1 no anduvo
esto anda: if (o1.toString > o2.toString) 1 else -1
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ABDLKOISF6IEMP53BOJMMJDQKWL47A5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7IJYQA#issuecomment-533765184>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABDLKONPZ75JDZNEBZHSM2LQKWL47ANCNFSM4IVQUROQ>
.
|
No tiene sentido, por eso mi propuesta inicial era devolver 1 como el mínimo cálculo posible. Pero seguramente se me pasó alguna letra chica del TreeSet y por eso el test que falla. Si corrige los tests, mi propuesta es pushearlo: ahora tenemos un montón de tests verdes más, solucionamos el problema original, no hay duplicados triviales para números, strings, booleanos, fechas, que es lo que el 96,89% de las veces vamos a usar, y démosle para adelante... |
Sí, tiene sentido lo que decís.
Esta tarde/noche tengo un rato para Wollok, dejame ver si puedo darle la
última vueltita para que quede 100%.
Propongo que aguantemos el finde así metemos eso y tal vez algo más.
Domingo a ultima hora o lunes a primera sale release.
El sáb., 21 de sep. de 2019 a la(s) 09:30, Fernando Dodino (
notifications@github.com) escribió:
… No tiene sentido, por eso mi propuesta inicial era devolver 1 como el
mínimo cálculo posible. Pero seguramente se me pasó alguna letra chica del
TreeSet y por eso el test que falla. Si corrige los tests, mi propuesta es
pushearlo: ahora tenemos un montón de tests verdes más, solucionamos el
problema original, no hay duplicados triviales para números, strings,
booleanos, fechas, que es lo que el 96,89% de las veces vamos a usar, y
démosle para adelante...
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ABDLKOIERL37XHOHTUAJ6NLQKYHWTA5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7IQ4YQ#issuecomment-533794402>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABDLKOJEQGYPWSNIL424NDTQKYHWTANCNFSM4IVQUROQ>
.
|
Dale, perfecto!! |
Bueno, tengo un caso que falla:
class Figura {
method superficie()
override method equals(other) = self.superficie() == other.superficie()
}
class Triangulo inherits Figura {
var property base
var property altura
override method superficie() = base * altura / 2
}
class Cuadrado inherits Figura {
var property lado
override method superficie() = lado * lado
}
Wollok interactive console (type "quit" to quit):
>> #{1,2,new Cuadrado(lado=3),3,"hola",1, new Pair(1,2), 6, "hola", 2323,
new Pair(1,2), [], 1, #{}, [], new Triangulo(base=3, altura=6), new
Cuadrado(lado=3), new Triangulo(base=4, altura=8),new Dictionary(), #{},
new Date(), new Dictionary()}
#{1, 2, 3, 6, 2323, a Pair[x=1, y=2], a Triangulo[altura=6, base=3], a
Triangulo[altura=8, base=4], a Dictionary[], "hola", a Cuadrado[lado=3],
[], #{}, a Cuadrado[lado=3], 9/21/2019}
>> #{1,2,new Cuadrado(lado=3),3,"hola",1, new Pair(1,2), 6, "hola", 2323,
new Pair(1,2), [], 1, #{}, [], new Triangulo(base=3, altura=6), new
Cuadrado(lado=3), new Triangulo(base=4, altura=8),new Dictionary(), #{},
new Date(), new Dictionary()}
#{a Cuadrado[lado=3], a Triangulo[altura=8, base=4], 1, 2, 3, 6, 2323, a
Pair[x=1, y=2], a Dictionary[], "hola", [], #{}, 9/21/2019}
>> #{1,2,new Cuadrado(lado=3),3,"hola",1, new Pair(1,2), 6, "hola", 2323,
new Pair(1,2), [], 1, #{}, [], new Triangulo(base=3, altura=6), new
Cuadrado(lado=3), new Triangulo(base=4, altura=8),new Dictionary(), #{},
new Date(), new Dictionary()}
#{1, 2, 3, 6, 2323, a Pair[x=1, y=2], a Cuadrado[lado=3], a
Triangulo[altura=8, base=4], a Dictionary[], "hola", [], #{}, 9/21/2019}
>> #{1,2,new Cuadrado(lado=3),3,"hola",1, new Pair(1,2), 6, "hola", 2323,
new Pair(1,2), [], 1, #{}, [], new Triangulo(base=3, altura=6), new
Cuadrado(lado=3), new Triangulo(base=4, altura=8),new Dictionary(), #{},
new Date(), new Dictionary()}
#{1, 2, 3, 6, 2323, a Cuadrado[lado=3], a Pair[x=1, y=2], a Dictionary[],
"hola", [], #{}, a Triangulo[altura=8, base=4], 9/21/2019}
>> #{1,2,new Cuadrado(lado=3),3,"hola",1, new Pair(1,2), 6, "hola", 2323,
new Pair(1,2), [], 1, #{}, [], new Triangulo(base=3, altura=6), new
Cuadrado(lado=3), new Triangulo(base=4, altura=8),new Dictionary(), #{},
new Date(), new Dictionary()}
#{1, 2, 3, 6, 2323, a Cuadrado[lado=3], a Triangulo[altura=8, base=4], a
Pair[x=1, y=2], a Dictionary[], "hola", [], #{}, 9/21/2019}
>> #{1,2,new Cuadrado(lado=3),3,"hola",1, new Pair(1,2), 6, "hola", 2323,
new Pair(1,2), [], 1, #{}, [], new Triangulo(base=3, altura=6), new
Cuadrado(lado=3), new Triangulo(base=4, altura=8),new Dictionary(), #{},
new Date(), new Dictionary()}
#{a Cuadrado[lado=3], 1, 2, 3, 6, 2323, a Triangulo[altura=6, base=3], a
Triangulo[altura=8, base=4], a Pair[x=1, y=2], a Dictionary[], "hola", [],
#{}, 9/21/2019}
>>
Como verán, en el caso es bastante complicado y posiblemente cae fuera de
la campana de Gauss.
Sin embargo creo que no estoy haciendo nada "incorrecto".
Si se fijan bien, ejecuto varias veces lo mismo en el REPL y da distintos
resultados.
En algunos identifica correctamente a los iguales, en otros no.
El sáb., 21 de sep. de 2019 a la(s) 10:07, Fernando Dodino (
notifications@github.com) escribió:
… Dale, perfecto!!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ABDLKOJDADWYFNCWKCXI25LQKYMCJA5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7IRO6Q#issuecomment-533796730>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABDLKOONS4LFYAZH3QOZOB3QKYMCJANCNFSM4IVQUROQ>
.
|
Bueno, tengo algo que da tests verdes. Y además tengo una definición precisa de lo que soportamos y lo que no. Las reglas para que un set funcione son:
Es decir, esto permite dos features interesantes pero que no se pueden usar al mismo tiempo, usando mi ejemplo anterior:
Si a = b, a != c y a.toString < c.toString, entonces b.toString < c.toString (o bien a.toString > c.toString y b.toString > c.toString. |
Entiendo que estas reglas son violentamente complicadas, pero surgen de la motivación de usar un tree set y no forzar a que todos los objetos sean comparables dentro del tree set. Habría que considerar la posibilidad de usar hashsets, tal vez las reglas serían más simples. La pregunta es si podemos hacerlo sin pedirle al estudiante que implemente eventualmente un hashCode. Finalmente, la última opción sería evitar tanto treesets como hashsets y comparar por fuerza bruta, pero esa estrategia la hemos intentado reemplazar por problemas de performance. Una variante para analizar sería que se usen colecciones inteligentes tipo treeset cuando tenemos colecciones homogéneas y migrar el wrapped a algo menos eficiente pero más simple de entender cuando se detecta la necesidad (es decir al ingresar un elemento heterogéneo en la colección). @fdodino faltaría verificar que los cambios que propongo no introducen problemas de performance. ¿Cómo puedo verificar eso? |
Mi test del ejemplo anterior anda, pero acá tengo un ejemplo que lo rompe:
Esto no es un bug, si compramos la especificación que escribí antes. Los triángulos y cuadrados no están respetando mis reglas. :) |
El jueves a la noche NicoV vino con un test "peor" que el que tiraste anoche, juntando en una colección números, booleanos, fechas, pares, dictionaries, sets, lists, strings, y no se cuántas cosas más. Yo lo miré y le dije: "y qué puede hacer un alumno con ésto", y medio que lo convencí de tirar para atrás ese test. Después de ver la seguidilla, me encanta ver que tenemos una justificación teórica de por qué eso no es algo que me interese testear: si tenemos objetos de una jerarquía (ej. Figura) que redefinen el igual y funciona, es lo que yo quiero. Después si tengo objetos polimórficos de diferentes jerarquías, no querría bajo ningún punto que redefinan el equals, y si lo hacen, creo que habría que advertirles que no usen un set.
|
Yo digo que mergiemos como está y luego pensemos cómo se mejora.
A mí me parece que podemos continuar en la línea de trabajos de performance
que vos empezaste y pensar en diferentes implementaciones internas para
diferentes tipos de datos en el set. Pero me gustaría que cambie
transparentemente.
Por lo menos habría dos casos, las colecciones homogéneas de objetos
ordenables o hasheables se pueden hacer con un set por debajo más piola y
si tenés objetos heterogéneos o no ordenables, que defaultea a una
implementación más simple pero que anda en todos los casos sin reglas
complejas.
Creo que los casos que nos complican son más bien escasos:
- Los pibes no suelen definir el equals ni comparable, los casos de
homogéneos ordenables van a ser casi siempre objetos definidos por nosotros
y que cumplen las reglas
- Las colecciones heterogéneas no son útiles, una colección con strings,
diccionarios, fechas, golondrinas no sirve para nada. Deberíamos soportarlo
porque los profes a veces usan esos ejemplos en las primeras clases, pero
no me calienta la performance.
- El caso más difícil son objetos definidos por el usuario que quieran
implementar equals => algo bastante improbable en obj1.
Con eso, el caso a pensar es el tercero, opciones:
- Ir por la definición no-performante, eso permite definir el equals a
piacere sin problemas.
- Definir reglas claras, que pueden ser las que tenemos hoy u otras más
simples/tradicionales (onda tenés que definir hashcode o bien el
greaterThan).
- Pensando libremente, si yo pudiera definir la igualdad declarativamente,
podría usar esa información para inferir la info extra que necesito para un
set performante.
El dom., 22 de sep. de 2019 a la(s) 12:48, Fernando Dodino (
notifications@github.com) escribió:
… @npasserini <https://github.com/npasserini> @nicovio
<https://github.com/nicovio>
El jueves a la noche NicoV vino con un test "peor" que el que tiraste
anoche, juntando en una colección números, booleanos, fechas, pares,
dictionaries, sets, lists, strings, y no se cuántas cosas más. Yo lo miré y
le dije: "y qué puede hacer un alumno con ésto", y medio que lo convencí de
tirar para atrás ese test. Después de ver la seguidilla, me encanta ver que
tenemos una justificación teórica de por qué eso no es algo que me interese
testear: si tenemos objetos de una jerarquía (ej. Figura) que redefinen el
igual y funciona, es lo que yo quiero. Después si tengo objetos
polimórficos de diferentes jerarquías, no querría bajo ningún punto que
redefinan el equals, y si lo hacen, creo que habría que advertirles que no
usen un set.
1. O sea, un issue probable sería agregar un warning en el validator
cuando eso ocurra.
2. Otra cosa que me gustaría es agregar esta definición en Set.
3. Y una tercera propuesta que tiro es: escribamos una clase BasicSet,
que herede de Set y tenga una implementación full hecha en Wollok. El
literal #{} te devuelve un Set inocente, si querés usar BasicSet tenés
que instanciarlo con new.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ABDLKOPZLQQAEJOTPMDW6BTQK6HVVA5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7JJANQ#issuecomment-533893174>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABDLKOKPIHUQIK7TG2P3NZTQK6HVVANCNFSM4IVQUROQ>
.
|
Me gusta mucho esa línea de pensamiento! No se me había ocurrido que el tener equals/greater than podría hacer cambiar la implementación de Set que usemos... +1 a mergear y a permitir que NicoV siga jugando con otras cosillas |
Estuve corriendo de nuevo los tests de acá: https://github.com/wollok/test-performance-set--1370. Comento los que tuvieron diferencias notables: Objetos definidos por el usuario sin == ni >Esperado: 1740 ms Objetos definidos por el usuario (redefiniendo == y >)Esperado: 6692 ms |
Me tiran un centro para pdoer correr esos tests?
El dom., 22 de sep. de 2019 a la(s) 15:56, Nicolas Viotti (
notifications@github.com) escribió:
… Estuve corriendo de nuevo los tests de acá:
https://github.com/wollok/test-performance-set--1370.
Comento los que tuvieron diferencias notables:
*Objetos definidos por el usuario con == sin el >*
Paso a tardar 133 segundos!!
*Objetos definidos por el usuario (redefiniendo == y >)*
Paso a tardar 15664 ms, cuando lo esperado era 6692 ms.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ABDLKOJ2KX7LO57JLTWXOBDQK65WTA5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7JMUOQ#issuecomment-533908026>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABDLKOJ2K5PM7RDUQGACLJLQK65WTANCNFSM4IVQUROQ>
.
|
yo ejecute un entorno Wollok embebido e importe el proyecto que paso Fer. Y ahí corrí los tests. pero ahora me surgió la duda de si esa era la forma correcta... veamos que dice @fdodino. |
Mmm... yo corro los tests pero no veo dónde está la comparación entre lo
que tarda y lo uqe debería tardar.
El dom., 22 de sep. de 2019 a la(s) 16:22, Nicolas Viotti (
notifications@github.com) escribió:
… Me tiran un centro para pdoer correr esos tests? El dom., 22 de sep. de
2019 a la(s) 15:56, Nicolas Viotti ( ***@***.***) escribió:
… <#m_7289814400876412856_>
Estuve corriendo de nuevo los tests de acá:
https://github.com/wollok/test-performance-set--1370. Comento los que
tuvieron diferencias notables: *Objetos definidos por el usuario con ==
sin el >* Paso a tardar 133 segundos!! *Objetos definidos por el usuario
(redefiniendo == y >)* Paso a tardar 15664 ms, cuando lo esperado era
6692 ms. — You are receiving this because you were mentioned. Reply to this
email directly, view it on GitHub <#1786
<#1786>?email_source=notifications&email_token=ABDLKOJ2KX7LO57JLTWXOBDQK65WTA5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7JMUOQ#issuecomment-533908026>,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABDLKOJ2K5PM7RDUQGACLJLQK65WTANCNFSM4IVQUROQ
.
yo ejecute un entorno Wollok embebido e importe el proyecto que paso Fer.
Y ahí corrí los tests.
pero ahora me surgió la duda de si esa era la forma correcta... veamos que
dice @fdodino <https://github.com/fdodino>.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ABDLKOLUKSAQBWH6AL7DTBLQK7AW3A5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7JNDSY#issuecomment-533909963>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABDLKOLV63B4G35WVANJC4TQK7AW3ANCNFSM4IVQUROQ>
.
|
Ah si, perdón. Lo esperado lo calcule con los resultados que posteo Dodino en #1370 (comment) Sume los que tardan las operaciones en cada test. De ahí sale lo que debería tardar. |
Sí, es así como los corrés. |
Bueno, acá tengo los tiempos ```
2000
[...1000 elements]
2000
#{...1000 elements}
|
TL;DR lo que anda mal es el max() sobre list, fundamentalmente con objetos que no definen el <. Asumo que tiene que ver con la creación de toStrings que se agregó. Se podría tunear, pero me parece un caso que no sólo cae fuera de la campana de Gauss sino que es ilógico (pedir el máximo de objetos que no definen <). Si quieren creamos un ticket para seguir iterando esto, pero yo mergearía esto como está. |
+1 a crear dos tickets:
Y definitivamente +1 a mergear |
Ah, y una cosa que podemos ver es cuánto tarda en agregar 10 elementos, que supongo que es lo más común, no debería superar medio segundo... |
en relacion al max() de list, me parece logico que falle cuando alguno de
los elementos nos tiene >. no me parece que sea algo a modificar. Es lo
mismo que el sum() cuando los elementos no se pueden sumar.
en todo lo de asSet y los criterios de igualdad, me perdí un poco, pero
entiendo que la razonamiento se extiende a otros métodos que se basan en la
igualdad. Por ejemplo, si se define una igualdad no simétrica, serian
varias las inconsistencias, como en el contains, en las claves de los
diccionarios, etc, Pero es responsabilidad del que se pone a jugar con
fuego sin saber. A lo sumo se podria aclarar cuáles son los metodos a tener
cuidado, restricciones, etc.
El lun., 23 sept. 2019 a las 6:44, Fernando Dodino (<
notifications@github.com>) escribió:
… Ah, y una cosa que podemos ver es cuánto tarda en agregar 10 elementos,
que supongo que es lo más común, no debería superar medio segundo...
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1786?email_source=notifications&email_token=ACZRXG2DGXSYKV5SYZ3O6HTQLCFYRA5CNFSM4IVQURO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7KKJGY#issuecomment-534029467>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACZRXG27BGEJK23OQDRPE3DQLCFYRANCNFSM4IVQUROQ>
.
|
Cambios:
asSet()
deja de ser native ywithoutDuplicates()
ya no lo usa.Agrego tests para
asSet()
ywithoutDuplicates()
Después de arreglar las inconsistencias relativas al issue, note este caso que seguía pasando:
Cuando el set era de distintos tipos, cada tanto se "comía" un duplicado (siempre eran Strings y Dates).
Lo pude solucionar con un cambio en el método
compare()
deWollokObjectComparator
. Hay que revisarlo.Fixes #1771 and #1783