(Inspirado por mi respuesta a esta pregunta ).
Considere este código (se supone que debe encontrar el elemento más grande que sea menor o igual que una entrada dada):
data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)
closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Nothing where
precise :: Maybe (Integer, v) -> TreeMap v -> Maybe (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
LT -> precise closestSoFar l
EQ -> Just (k, v)
GT -> precise (Just (k, v)) r
Esto no es muy vago. Una vez que GTse ingresa el caso, sabemos con certeza que el valor de retorno final será Justalgo más que Nothing, pero Justaún no estará disponible hasta el final. Me gustaría hacerlo más perezoso para que Justesté disponible tan pronto como GTse ingrese el caso. Mi caso de prueba para esto es que quiero Data.Maybe.isJust $ closestLess 5 (Node 3 () Leaf undefined)evaluar en Truelugar de tocar fondo. Aquí hay una forma en que puedo pensar en hacer esto:
data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)
closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess _ Leaf = Nothing
closestLess i (Node k v l r) = case i `compare` k of
LT -> closestLess i l
EQ -> Just (k, v)
GT -> Just (precise (k, v) r)
where
precise :: (Integer, v) -> TreeMap v -> (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
LT -> precise closestSoFar l
EQ -> (k, v)
GT -> precise (k, v) r
Sin embargo, ahora me estoy repitiendo: la lógica central ahora está en ambos closestLessy en precise. ¿Cómo puedo escribir esto para que sea flojo pero sin repetirme?
fuente

A partir de mi implementación no perezosa, primero refactoré
precisepara recibirJustcomo argumento, y generalicé su tipo en consecuencia:Luego, lo cambié para hacerlo
wraptemprano y llamarloiden elGTcaso:Esto todavía funciona exactamente como antes, excepto por el beneficio de la pereza adicional.
fuente
ids en el medioJusty la final son(k,v)eliminados por el compilador? Probablemente no, se supone que las funciones son opacas, y podría haberlo utilizado (de forma factible) enfirst (1+)lugar deidtodo lo que el compilador sabe. pero es un código compacto ... por supuesto, mi código es el de desentrañar y especificar el suyo aquí, con la simplificación adicional (la eliminación de losids). También es muy interesante cómo el tipo más general sirve como una restricción, una relación entre los valores involucrados (aunque no lo suficientemente ajustados, con elfirst (1+)permiso comowrap).precisese usa en dos tipos, que corresponden directamente a las dos funciones especializadas utilizadas en la variante más detallada. Buena interacción allí. Además, no llamaría a esto CPS,wrapno se usa como una continuación, no está construido "en el interior", está apilado, por recursividad, en el exterior. Tal vez si se utiliza como continuación usted podría deshacerse de esas extrañasids ... por cierto podemos ver aquí una vez más que viejo patrón del argumento funcional utilizado como indicador de qué hacer, el cambio entre las dos líneas de acción (Justoid).Creo que la versión de CPS que respondió usted mismo es la mejor, pero para completar, aquí hay algunas ideas más. (EDITAR: la respuesta de Buhr es ahora la más eficaz).
La primera idea es deshacerse del "
closestSoFar" acumulador y, en su lugar, dejar que elGTcaso maneje toda la lógica de elegir el valor de la derecha más pequeño que el argumento. De esta forma, elGTcaso puede devolver directamente unJust:Esto es más simple, pero ocupa un poco más de espacio en la pila cuando llegas a muchos
GTcasos. Técnicamente, incluso podría usar esofromMaybeen forma de acumulador (es decir, reemplazar lofromJustimplícito en la respuesta de luqui), pero eso sería una rama redundante e inalcanzable.La otra idea es que en realidad hay dos "fases" del algoritmo, una antes y otra después de presionar a
GT, por lo que lo parametrizas con un booleano para representar estas dos fases, y usas tipos dependientes para codificar el invariante de que siempre habrá un resultado en la segunda fase.fuente
Qué tal si
?
fuente
Justpero es total. Sé que su solución tal como está escrita es de hecho total, pero es frágil ya que una modificación aparentemente segura podría resultar en un fondo.Just, por lo que agregará una prueba para asegurarse de que noNothingse repita cada vez.No solo sabemos siempre
Just, después de su primer descubrimiento, también siempre sabemosNothinghasta entonces. Eso es en realidad dos "lógicas" diferentes.Entonces, primero vamos a la izquierda, así que explícalo:
El precio es que repetimos como máximo un paso como máximo una vez.
fuente