¿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 .
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.
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]+[/]
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).
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.
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.
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
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.
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!']
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.
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.
import StdEnv
+a and b
(21 bytes) menor que%[a:r]|a= %r=a;%_=True
(22 bytes)? ¿O seríaimport StdEnv
+a=True and b=True
(31 bytes), en cuyo caso es definitivamente más corto? (Nunca he programado en Clean, por cierto)map f list -> [f x\\x<-list] (11 bytes saved)
(o algo similar)).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:
[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]+[/]
fuente
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
String
y[Char]
literales ("stuff"
y['stuff']
respectivamente), cualquier bytes excepto 0 están permitidos, con la advertencia de que"
y'
deben escaparse (paraString
y[Char]
respectivamente), y que los saltos de línea y retornos carraige deben ser reemplazados con\n
y\r
(también respectivamente).En
Char
literales, se permite cualquier byte excepto 0 , lo que significa que: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
1
en aString
, 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
String
y 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).fuente
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^2
es más corto quef 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+?b
analiza 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
StdEnv
son@$?
. La sobrescritura^-+
(etc.) puede ser útil si necesita más identificadores simbólicos, pero tenga cuidado de no sobrescribir uno que esté utilizando.fuente
Conoce tus
KnodosAlgunas 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
Observe cómo la versión con
#
es más corta y cómo podemos redefinirlas
. Esto es útil si no necesitamos el valor que tiene una variable cuando la recibimos, por lo que podemos reutilizar el nombre. (let
puede tener problemas cuando haces eso)Pero usar
let
es 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:
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.fuente
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. ElText
módulo soluciona esto.Conversión:
Text
define el operador<+
para cualquiera de los dos tipos que se hantoString
definido.Este operador, utilizado como
a<+b
es lo mismo quetoString 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:
Text
define algunas grapas de manipulación de cadenas que faltan enStdEnv
:+
para cadenas, que es mucho más corto que+++
(desdeStdEnv
)indexOf
, con el comportamiento tipo C de regresar en-1
lugar deNothing
fallarconcat
, que concatena una lista de cadenasjoin
, que une una lista de cadenas con una cadena de separaciónsplit
, que divide una cadena en una lista de cadenas en una subcadenafuente
Usa lambdas más cortas
A veces te encuentras usando una expresión lambda (para pasar a
map
, osortBy
, 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 cortoLa otra forma correcta:
Si estás usando
Data.Func
, también puedes hacerEl camino corto:
Esto es lo mismo, pero con una sintaxis de golfista
La otra manera:
El uso de la composición no es más corto esta vez, pero a veces puede ser más corto
La otra manera:
Si bien es un poco artificial aquí, puedes usar guardias en lambdas
Y también expresiones de nodo let-before
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.fuente
\a=...
era la sintaxis lambda habitual de Clean: P->
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).on(<)length
, aunque laData.Func
importación lo separará a menos que ya lo necesite.#
) en lambdas.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!']
fuente
A veces
code
es más cortoClean 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*World
en code-golf generalmente es una mala idea de todos modos.Para solucionar este problema, a menudo hay
ccall
s que puedes usar dentro decode
bloques.Algunos ejemplos:
Hora del sistema
Lo anterior es de 58 bytes, pero puede guardar 17 bytes (hasta 40 + 1) con:
Números al azar
Este no guarda bytes por sí solo, pero evita tener que pasar una lista de
genRandInt
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.fuente