Consejos para jugar golf en Clean

17

¿Qué consejos generales tienes para jugar al golf en Clean? Publique solo ideas que se puedan aplicar a los problemas de código de golf en general, y que sean al menos algo específicas de Clean.

Si nunca ha oído hablar de Clean, puede encontrar más información aquí .
O puede unirse a la sala de chat .

Οurous
fuente

Respuestas:

10

Evitar import StdEnvcuando sea posible

Para acceder a las funciones integradas, incluso las que parecen básicas, como (==)o map, se necesita una declaración de importación, generalmente import StdEnvporque importa los módulos más comunes StdInt, StdBooletc. (consulte aquí para obtener más información StdEnv).

Sin embargo, puede ser posible evitar esta importación para algunos desafíos y simplemente usar las características principales del lenguaje, como la comprensión de listas y la coincidencia de patrones.

Por ejemplo, en lugar de

import StdEnv 
map f list

uno puede escribir

[f x\\x<-list]

Listado de alternativas:

Algunas funciones o invocaciones de funciones que necesitan import StdEnv, una alternativa que no necesita la importación y una estimación aproximada de bytes guardados.

  • hd-> (\[h:_]=h), ~ 6 bytes
  • tl-> (\[_:t]=t), ~ 6 bytes
  • map f list-> [f x\\x<-list], ~ 10 bytes
  • filter p list-> [x\\x<-list|p x], ~ 11 bytes
  • (&&)-> %a b|a=b=a;%, ~ 6 bytes
  • (||)-> %a b|a=a=b;%, ~ 6 bytes
  • not-> %a|a=False=True;%, ~ 1 byte
  • and-> %[a:r]|a= %r=a;%_=True, ~ 0 bytes
  • or-> %[a:r]|a=a= %r;%_=False, ~ 0 bytes

Es poco probable que los últimos ahorren bytes, ya que un reemplazo directo produce más bytes que la importación, pero podría ser posible en los casos en que la recursión sobre la lista sea necesaria de todos modos.

Este consejo se ha utilizado con éxito aquí .

Laikoni
fuente
Sin embargo, ¿no es import StdEnv+ a and b(21 bytes) menor que %[a:r]|a= %r=a;%_=True(22 bytes)? ¿O sería import StdEnv+ a=True and b=True(31 bytes), en cuyo caso es definitivamente más corto? (Nunca he programado en Clean, por cierto)
Kevin Cruijssen
@KevinCruijssen Estábamos discutiendo eso en el chat . Es cierto que es poco probable que ahorren bytes, excepto tal vez cuando el programa necesita repetirse en una lista de todos modos.
Laikoni
44
Ah ok Quizás también sea útil indicar cuántos bytes se guardan con la alternativa (es decir map f list -> [f x\\x<-list] (11 bytes saved)(o algo similar)).
Kevin Cruijssen
@KevinCruijssen Hecho.
Laikoni
5

Saber aprender el idioma

Después de todo, ¿cómo puede alguien jugar al golf en un idioma que no puede usar?

En línea

Clean no es un lenguaje bien conocido o bien documentado, y el nombre ciertamente no facilita encontrar recursos muy necesarios para remediar estos problemas ... ¿o sí?

Originalmente, Clean se llamaba Concurrent Clean , que todavía se usa en el prefacio de casi todos los documentos relacionados con Clean, por lo que si está buscando Clean, busque Concurrent Clean en su lugar.

Una de las similitudes más notables de Clean con Haskell (de las cuales hay muchas) es la existencia de Cloogle , que es un motor de búsqueda de funciones que cubre las bibliotecas con las que se distribuye Clean.

En la zona

Las bibliotecas con las que se distribuye Clean tienen la forma de archivos de origen de Clean, bien comentados y algo auto documentados, que se pueden examinar mediante el IDE.
(También viene con programas de ejemplo completos, debajo $INSTALL/Examples).

Hablando de eso, la versión de Windows de Clean viene con un IDE, aunque está bastante limitada por los estándares modernos, es mucho mejor que usar un editor de texto y la línea de comandos.
Las dos características más útiles (en el contexto del aprendizaje) son:

  • Puede hacer doble clic en un error para ver en qué línea está
  • Puede resaltar el nombre de un módulo y presionar [Ctrl]+[D]para abrir el archivo de definición (o usar [Ctrl]+[I]para el archivo de implementación), y alternar entre la definición y el archivo de implementación con[Ctrl]+[/]
Οurous
fuente
4

Olvídate de la codificación de caracteres

Al compilador de Clean no le importa qué codificación cree que ha guardado el archivo de origen, solo los valores de bytes en el archivo. Esto tiene algunas buenas consecuencias.

En el cuerpo del código fuente, solo se permiten bytes con puntos de código correspondientes a los caracteres ASCII imprimibles, además de aquellos para \t\r\n.

Literales:

En Stringy [Char]literales ( "stuff"y ['stuff']respectivamente), cualquier bytes excepto 0 están permitidos, con la advertencia de que "y 'deben escaparse (para Stringy [Char]respectivamente), y que los saltos de línea y retornos carraige deben ser reemplazados con \ny \r(también respectivamente).

En Charliterales, se permite cualquier byte excepto 0 , lo que significa que:

'\n'

'
'

Son lo mismo, pero el segundo es un byte más corto.

Escapar

Aparte de los escapes de letras estándar \t\r\n(etc.), todas las secuencias de escape no numéricas en Clean son para la barra diagonal o para la cita utilizada para delimitar el literal en el que se encuentra el escape.

Para secuencias de escape numéricas, el número se trata como un valor octal terminado después de tres dígitos. Esto significa que si desea un valor nulo seguido del carácter 1en a String, debe usar "\0001"(o "\0\61") y no "\01" . Sin embargo, si sigue el escape con cualquier cosa que no sean números, puede omitir los ceros iniciales.

Consecuencias:

Esta peculiaridad con la forma en que Clean maneja sus archivos fuente permite Stringy se ['Char']convierte efectivamente en secuencias de números de un solo dígito de base 256, que tiene una multitud de usos para el golf de código, como el almacenamiento de índices (hasta 255, por supuesto).

Οurous
fuente
3

Nombre de funciones con símbolos

Al definir una función, a menudo es más corto usar alguna combinación de !@$%^&*~-+=<:|>.?/\caracteres alfanuméricos, ya que le permite omitir espacios en blanco entre los identificadores.

Por ejemplo: ?a=a^2es más corto que f a=a^2, e invocarlo también es más corto.

Sin embargo :

Si el identificador de función se usa junto a otros símbolos, que pueden combinarse para formar un identificador diferente pero válido , todos se analizarán como un identificador y verá un error.

Por ejemplo: ?a+?banaliza como? a +? b

Adicionalmente:

Es posible sobrescribir los identificadores importados en Clean, por lo que los únicos identificadores de símbolos de un solo carácter que ya no se usan StdEnvson @$?. La sobrescritura ^-+(etc.) puede ser útil si necesita más identificadores simbólicos, pero tenga cuidado de no sobrescribir uno que esté utilizando.

Οurous
fuente
3

Conoce tus K nodos

Algunas de las construcciones más fuertes (para golf) en lenguajes funcionales son let ... in ....
Limpio, por supuesto, tiene esto y algo mejor: el #.

¿Qué es un nodo?

Clean #y ubicuo |(patrón de protección) se conocen como 'expresiones de nodo'.
Cabe destacar, que le permiten programar imperatively- ish en Limpio (que es realmente bueno aquí!).

El #(let-before):

Ambos calculan el valor de un entero dado como una cadena, multiplicado por la suma de sus caracteres

f s=let i=toInt s;n=sum[toInt c\\c<-:s]in n*i

f s#i=toInt s
#s=sum[toInt c\\c<-:s]
=s*i

Observe cómo la versión con #es más corta y cómo podemos redefinirla s. Esto es útil si no necesitamos el valor que tiene una variable cuando la recibimos, por lo que podemos reutilizar el nombre. ( letpuede tener problemas cuando haces eso)

Pero usar letes más fácil cuando necesitas algo comoflip f = let g x y = f y x in g

El |(protector de patrón):

El patrón de protección de Clean se puede usar como los de muchos otros lenguajes funcionales; sin embargo, también se puede usar como un imperativo if ... else .... Y una versión más corta de la expresión ternaria.

Por ejemplo, todos estos devuelven el signo de un entero:

s n|n<>0|n>0=1= -1
=0

s n=if(n<>0)if(n>0)1(-1)0

s n|n>0=1|n<0= -1=0

Por supuesto, el último que usa la guardia más tradicionalmente es el más corto, pero el primero muestra que puede anidarlos (pero solo pueden aparecer dos cláusulas de retorno incondicionales en la misma línea en la regla de diseño), y el segundo muestra qué el primero lo hace lógicamente.

Una nota:

Puedes usar estas expresiones básicamente en cualquier lugar. En lambdas, case ... of, let ... in, etc.

Οurous
fuente
1

Si estás usando un String, deberías estar usandoText

La conversión a cadenas y la manipulación de cadenas (el tipo {#Char}/ String, no el [Char]tipo) es bastante larga y mala para el golf. El Textmódulo soluciona esto.

Conversión:

Textdefine el operador <+para cualquiera de los dos tipos que se han toStringdefinido.
Este operador, utilizado como a<+bes lo mismo que toString a+++toString b: guardar al menos 19 bytes . Incluso si incluye la importación adicional ,Text, y la usa solo una vez, ¡ aún guarda 14 bytes!

Manipulación:

Textdefine algunas grapas de manipulación de cadenas que faltan en StdEnv:

  • El operador +para cadenas, que es mucho más corto que +++(desde StdEnv)
  • indexOf, con el comportamiento tipo C de regresar en -1lugar de Nothingfallar
  • concat, que concatena una lista de cadenas
  • join, que une una lista de cadenas con una cadena de separación
  • split, que divide una cadena en una lista de cadenas en una subcadena
Οurous
fuente
1

Usa lambdas más cortas

A veces te encuentras usando una expresión lambda (para pasar a map, o sortBy, etc.). Cuando estás haciendo esto (escribiendo lambdas), hay muchas maneras de hacerlo.

La direccion correcta:

Esto es sortBy, con una lambda idiomática que ordena las listas del más largo al más corto

sortBy (\a b = length a > length b)

La otra forma correcta:

Si estás usando Data.Func, también puedes hacer

sortBy (on (>) length)

El camino corto:

Esto es lo mismo, pero con una sintaxis de golfista

sortBy(\a b=length a>length b)

La otra manera:

El uso de la composición no es más corto esta vez, pero a veces puede ser más corto

sortBy(\a=(>)(length a)o length)

La otra manera:

Si bien es un poco artificial aquí, puedes usar guardias en lambdas

sortBy(\a b|length a>length b=True=False)

Y también expresiones de nodo let-before

sortBy(\a b#l=length
=l a>l b)

Una nota:

Hay dos formas más de lambda, (\a b . ...)y (\a b -> ...)la última de las cuales es idéntica a la =variante, y la primera existe por alguna razón y a menudo parece que está intentando acceder a una propiedad de algo en lugar de definir una lambda, así que no No lo use.

Οurous
fuente
1
Después de ver algunos de sus programas de golf, tuve la impresión de que \a=... era la sintaxis lambda habitual de Clean: P
Ørjan Johansen
También puede agregar los guardias en lambda, como se usa aquí . Esto no está documentado (incluso contradice el informe del idioma), pero funciona. Además, ->y =para lambdas son idénticos en lo que respecta al compilador ( ->es una sintaxis antigua). Solo .es diferente (pero no sé exactamente cómo).
Y en este ejemplo particular, podría considerar usar on(<)length, aunque la Data.Funcimportación lo separará a menos que ya lo necesite.
@Keelan Cool. Actualizaré esto más tarde hoy. Creo que también puedes usar let-before ( #) en lambdas.
Precioso
Sí, puedes :-)
0

Usar literales de lista de caracteres

Una lista literal de caracteres es una forma abreviada de escribir algo así como ['h','e','l','l','o']as ['hello'].

Este no es el límite de la notación, por ejemplo:

  • repeat'c'se ['c','c'..]convierte se convierte['cc'..]
  • ['z','y'..'a'] se convierte ['zy'..'a']
  • ['beginning']++[a,b,c]++['end'] se convierte ['beginning',a,b,c,'end']
  • ['prefix']++suffix se convierte ['prefix':suffix]

Estos también funcionan en la coincidencia:

  • ['I don't care about the next character',_,'but I do care about these ones!']
Οurous
fuente
0

A veces codees más corto

Clean tiene un montón de funciones realmente útiles en las bibliotecas estándar, algunas de las cuales son increíblemente detalladas para usar sin acceso *World, y usar *Worlden code-golf generalmente es una mala idea de todos modos.

Para solucionar este problema, a menudo hay ccalls que puedes usar dentro de codebloques.

Algunos ejemplos:

Hora del sistema

import System.Time,System._Unsafe
t=toInt(accUnsafe(time))

Lo anterior es de 58 bytes, pero puede guardar 17 bytes (hasta 40 + 1) con:

t::!Int->Int
t _=code{ccall time "I:I"
}

Números al azar

Este no guarda bytes por sí solo, pero evita tener que pasar una lista de genRandInt

s::!Int->Int
s _=code{ccall time "I:I"ccall srand "I:I"
}
r::!Int->Int
r _=code{ccall rand "I:I"
}

Otros usos

Además de estos dos, que probablemente son los usos principales de esto en code-golf, puede llamar a cualquier función con nombre (incluidas, entre otras, cada llamada al sistema), incrustar ensamblaje arbitrario instruction <byte>e incrustar código para la máquina ABC.

Οurous
fuente