Un programa escrito en Haskell, que imprime un programa en Perl, que imprime…

¿Un programa escrito en Haskell, que al ejecutarse imprime un programa en Perl, que al ejecutarse imprime un programa en Python, que al ejecutarse imprime un programa en Ruby, que al ejecutarse imprime un programa en C, que al ejecutarse imprime un programa en Java, que al ejecutarse imprime el programa original? Sí, es como para que te duela la cabeza, aunque el código, que podéis ver después del salto, es de lo más sencillo.

Este tipo de programas que se imprimen a sí mismos como salida (aunque en este caso, con una peculiaridad extra) se conocen con el nombre de quines, en honor al filósofo estadounidense Willard Van Orman Quine.

import Data.List

langs = [Haskell, Perl, Python, Ruby, C, Java]

data Languages = Haskell | Ruby | Perl | C | Python | Java

sequenceFromString Haskell s = "map toEnum[" ++ (intercalate "," $
    map (\c -> show (fromEnum c)) s) ++ "]"
sequenceFromString Perl s    = (intercalate "," $
    map (\c -> "chr(" ++ show (fromEnum c) ++ ")") s)
sequenceFromString Python s  = (intercalate "+" $
    map (\c -> "chr(" ++ show (fromEnum c) ++ ")") s)
sequenceFromString Ruby s    = (intercalate "+" $
    map (\c -> show (fromEnum c) ++ ".chr") s)
sequenceFromString C s       = concatMap
    (\c -> "putchar(" ++ show (fromEnum c) ++ ");") s
sequenceFromString Java s    = concatMap
    (\c -> "o.write(" ++ show (fromEnum c) ++ ");") s

paramList' Haskell = intercalate " " . map (\n -> "a" ++ show n)
paramList' C       = intercalate "," . map (\n -> "char *a" ++ show n)
paramList' Python  = intercalate "," . map (\n -> "a" ++ show n)
paramList' Ruby    = intercalate "," . map (\n -> "a" ++ show n)
paramList' Java    = intercalate "," . map (\n -> "String a" ++ show n)

paramList Perl    _ = ""
paramList lang n = paramList' lang [0..n-1]

driver l args = defn l ++ intercalate (divider l) args ++ endDefn l

divider C       = "\",\""
divider Perl    = "','"
divider Ruby    = "\",\""
divider Python  = "\",\""
divider Haskell = "\" \""
divider Java    = "\",\""

defn C       = "main(){q(\""
defn Perl    = "&q('"
defn Python  = "q(\""
defn Ruby    = "q(\""
defn Haskell = "main=q \""
defn Java    = "public static void main(String[]args){q(\""

endDefn C       = "\");}"
endDefn Perl    = "')"
endDefn Python  = "\")"
endDefn Ruby    = "\")"
endDefn Haskell = "\""
endDefn Java    = "\");}}"

arg Haskell n = "a" ++ show n
arg Perl n    = "$_[" ++ show n ++ "]"
arg C n       = "printf(a" ++ show n ++ ");"
arg Python n  = "a" ++ show n
arg Ruby n    = "a" ++ show n
arg Java n    = "o.print(a" ++ show n ++ ");"

argDivide Haskell l = "++" ++ sequenceFromString Haskell (divider l) ++ "++"
argDivide Perl l    = ","    ++ sequenceFromString Perl (divider l) ++ ","
argDivide C l       = sequenceFromString C (divider l)
argDivide Python l  = "+" ++ sequenceFromString Python (divider l) ++ "+"
argDivide Ruby l    = "+" ++ sequenceFromString Ruby (divider l) ++ "+"
argDivide Java l    = sequenceFromString Java (divider l)

argList lang1 lang2 n = intercalate (argDivide lang1 lang2) $
    map (arg lang1) ([1..n-1] ++ [0])

fromTo Haskell l n = "q " ++ paramList Haskell n ++ "=putStrLn$a0++" ++
    sequenceFromString Haskell ("\n" ++ defn l) ++ "++" ++
    argList Haskell l n ++ "++" ++ sequenceFromString Haskell (endDefn l)
fromTo Perl    l n = "sub q {" ++ "print $_[0]," ++
    sequenceFromString Perl ("\n" ++ defn l) ++ "," ++ argList Perl l n ++ "," ++
    sequenceFromString Perl (endDefn l ++ "\n") ++ "}"
fromTo Python  l n = "def q(" ++ paramList Python n ++
    "): print a0+" ++ sequenceFromString Python ("\n" ++ defn l) ++
    "+" ++ argList Python l n ++ "+" ++ sequenceFromString Python (endDefn l)
fromTo Ruby    l n = "def q(" ++ paramList Ruby n ++
    ") print a0+" ++ sequenceFromString Ruby ("\n" ++ defn l) ++
    "+" ++ argList Ruby l n ++ "+" ++ sequenceFromString Ruby (endDefn l ++ "\n") ++ " end"
fromTo C       l n = "q(" ++ paramList C n ++ "){" ++ "printf(a0);" ++
    sequenceFromString C ("\n" ++ defn l) ++ argList C l n ++
    sequenceFromString C (endDefn l ++ "\n") ++ "}"
fromTo Java    l n = "public class quine{public static void q(" ++
    paramList Java n ++ "){java.io.PrintStream o=System.out;o.print(a0);" ++
    sequenceFromString Java ("\n" ++ defn l) ++ argList Java l n ++
    sequenceFromString Java (endDefn l ++ "\n") ++ "}"

main = do
    let n = length langs
    let langs' = cycle langs
    putStrLn $ fromTo (head langs') (head (tail langs')) n
    putStrLn $ driver (head langs') $ zipWith (\lang1 lang2 -> fromTo lang1 lang2 n)
        (take n (tail langs')) (tail (tail langs'))

Vía Hacker News



Comentarios
  1. […] Un programa escrito en Haskell, que imprime un programa en Perl, un programa en Python, que al ejecu… mundogeek.net/archivos/2011/03/15/un-programa-escrito-en-…  por mezvan hace 2 segundos […]

    Responder

  2. Mikel

    ¡Que cansado! ¿No se puede redirigir a un fichero en vez de a la impresora?

    Responder

    • boned

      Imprimir = sacar por salida estándar.

      Y desde hace algunas décadas, la salida estándar suele ser la pantalla (terminal de texto)

      Responder

    • anon

      omg…

      Responder

    • rober

      Que hdp.

      Responder

  3. Eduardiyo

    Me gustaría saber qué has entendido tú por imprimir…

    Responder

    • peperete

      Sacar por pantalla. De hecho el método se llama print 😛

      Responder

  4. Haskell, un lenguaje de programación con el que tengo una relación de amor/odio.

    Responder

    • ¿Y eso?

      Responder

      • Porque en la carrera tuve una interesantísima asignatura (Técnicas de Simulación) en la que, entre otras cosas, nos enseñaban el paradigma de la programación funcional mediante Haskell. A mi nunca me ha gustado aprender algo “por la fuerza”, así que mientras tuve que entregar prácticas llegué a detestar Haskell (creo que la programación funcional en sí); pero, una vez aprobé la asignatura, me he puesto a investigar cosicas y cada vez me gusta más. Sobre todo en el campo matemático, o para al crear modelos del “mundo real”.
        Lo mismo se puede aplicar a LISP. 😛

        Responder

        • En LISP coincido. Me gustaba el lenguaje, pero se me iban los ojos con tanto paréntesis.

          Responder

  5. […] » noticia original […]

    Responder

  6. hugo

    Solo Haskell es tan grande.

    Responder

    • alberto

      ¿Te das cuenta de que no está escrito en haskell si no en todos ellos a la vez?

      Responder

      • hugo

        ¿Haz programado en Haskell?
        Solo asi sabrias que este lenguaje hace facil lo imposible y lo facil imposible.

        Responder

  7. kameb

    amigo lo corro en codepad.org y me manda error en la linea 7

    Output:

    1Error occurred
    2ERROR line 7 – Undefined variable “intercalate”

    http://codepad.org/FZNXJt43

    Responder

    • Es parte de Data.List, así que parece que codepad pasa del import Data.List. O quizás use una versión muy antigua.

      Responder

  8. Sergio

    Vaya chorrada. Qué manera de complicarse para programar un bucle… ¡si hasta a mí me salen sin querer!

    Responder

  9. malamuteee

    haskel no era un perro ?

    jajajaja

    Responder

  10. helq

    Después de juguetear un rato en la red, he encontrado esté programa “quine”:
    Es alucinante ( http://d.hatena.ne.jp/ku-ma-me/20090916/p1 )

    PD: Apartir del programa que ha puesto @zootropo me he hecho uno en python que se duplica, y no más :D, no estoy en el nivel de poder hacer más. Y debo decirte @zoo que el programa que pusiste lo ejecuté hasta cuando “cataplum” intentándolo correr con java, no se dejo ¬¬

    Responder

Deja un comentario