Golfscript - 12 caracteres
{,1\{)*}/}:f
Comenzando con Golfscript - Factorial paso a paso
Aquí hay algo para las personas que están tratando de aprender golfscript. El requisito previo es una comprensión básica de golfscript y la capacidad de leer la documentación de golfscript.
Por eso queremos probar nuestra nueva herramienta golfscript . Siempre es bueno comenzar con algo simple, así que comenzamos con factorial. Aquí hay un intento inicial, basado en un pseudocódigo imperativo simple:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
El espacio en blanco se usa muy raramente en golfscript. El truco más fácil para deshacerse del espacio en blanco es usar diferentes nombres de variables. Cada token se puede usar como una variable (ver la página de sintaxis ). Fichas útiles para utilizar como variables son caracteres especiales como |
, &
, ?
- por lo general no cualquier cosa usada en otras partes del código. Estos siempre se analizan como tokens de un solo personaje. En contraste, las variables como n
requerirán un espacio para insertar un número en la pila después. Los números son esencialmente variables preinicializadas.
Como siempre, habrá declaraciones que podemos cambiar, sin afectar el resultado final. En golfscript, todo lo que se evalúa como verdadera, excepto 0
, []
, ""
, y {}
(ver esta ). Aquí, podemos cambiar la condición de salida de bucle a simplemente {n}
(bucleamos un tiempo adicional y terminamos cuando n = 0).
Al igual que con el golf en cualquier idioma, es útil conocer las funciones disponibles. Afortunadamente, la lista es muy corta para golfscript. Podemos cambiar 1-
a (
para salvar a otro personaje. En la actualidad, el código se ve así: (podríamos usarlo en 1
lugar de |
aquí si quisiéramos, lo que dejaría la inicialización).
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
Es importante usar bien la pila para obtener las soluciones más cortas (práctica práctica). En general, si los valores solo se usan en un pequeño segmento de código, puede que no sea necesario almacenarlos en variables. Al eliminar la variable de producto en ejecución y simplemente usar la pila, podemos guardar una gran cantidad de caracteres.
{:n;1{n}{n*n(:n;}while}:f
Aquí hay algo más en lo que pensar. Estamos eliminando la variable n
de la pila al final del cuerpo del bucle, pero luego la empujamos inmediatamente después. De hecho, antes de que comience el ciclo, también lo eliminamos de la pila. En su lugar, deberíamos dejarlo en la pila, y podemos mantener en blanco la condición del bucle.
{1\:n{}{n*n(:n}while}:f
Quizás incluso podamos eliminar la variable por completo. Para hacer esto, necesitaremos mantener la variable en la pila en todo momento. Esto significa que necesitamos dos copias de la variable en la pila al final de la verificación de condición para que no la perdamos después de la verificación. Lo que significa que tendremos un redundante 0
en la pila después de que finalice el ciclo, pero eso es fácil de solucionar.
¡Esto nos lleva a nuestra while
solución de bucle óptima !
{1\{.}{.@*\(}while;}:f
Ahora todavía queremos hacer esto más corto. El objetivo obvio debería ser la palabra while
. Mirando la documentación, hay dos alternativas viables: desplegar y hacer . Cuando tenga la opción de elegir diferentes rutas, intente sopesar los beneficios de ambas. Desplegar es 'casi un bucle while', por lo que, como estimación, reduciremos el carácter while
de 5 en 4 /
. En cuanto a do
, cortamos while
en 3 caracteres y fusionamos los dos bloques, lo que podría salvar a otro personaje o dos.
En realidad, hay un gran inconveniente al usar un do
bucle. Dado que la verificación de la condición se realiza después de que el cuerpo se ejecuta una vez, el valor de 0
será incorrecto, por lo que es posible que necesitemos una instrucción if. Te diré ahora que el despliegue es más corto ( do
al final se proporcionan algunas soluciones ). Siga adelante y pruébelo, el código que ya tenemos requiere cambios mínimos.
{1\{}{.@*\(}/;}:f
¡Excelente! Nuestra solución ahora es súper corta y hemos terminado aquí, ¿verdad? No. Esto tiene 17 caracteres y J tiene 12 caracteres. ¡Nunca admitas la derrota!
Ahora estás pensando con ... recursividad
Usar recursividad significa que debemos usar una estructura de ramificación. Desafortunadamente, pero como el factorial se puede expresar de manera tan breve y recursiva, parece una alternativa viable a la iteración.
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
Bueno, eso fue fácil: si hubiéramos intentado la recursividad antes, ¡tal vez ni siquiera hubiéramos visto usar un while
bucle! Aún así, solo tenemos 16 caracteres.
Matrices
Las matrices generalmente se crean de dos maneras: usando los caracteres [
y ]
, o con la ,
función. Si se ejecuta con un número entero en la parte superior de la pila, ,
devuelve una matriz de esa longitud con arr [i] = i.
Para iterar sobre matrices, tenemos tres opciones:
{block}/
: empujar, bloquear, empujar, bloquear, ...
{block}%
: [push, block, push, block, ...] (esto tiene algunos matices, por ejemplo, los valores intermedios se eliminan de la pila antes de cada inserción)
{block}*
: empujar, empujar, bloquear, empujar, bloquear, ...
La documentación de golfscript tiene un ejemplo de uso {+}*
para sumar el contenido de una matriz. Esto sugiere que podemos usar {*}*
para obtener el producto de una matriz.
{,{*}*}:f
Desafortunadamente, no es tan simple. Todos los elementos están separados por uno (en [0 1 2]
lugar de [1 2 3]
). Podemos usar {)}%
para rectificar este problema.
{,{)}%{*}*}:f
Pues no del todo. Esto no maneja cero correctamente. Podemos calcular (n + 1)! / (N + 1) para rectificar esto, aunque esto cuesta demasiado.
{).,{)}%{*}*\/}:f
También podemos tratar de manejar n = 0 en el mismo depósito que n = 1. Esto es realmente extremadamente corto de hacer, prueba y trabaja lo más corto que puedas.
No tan buena es la clasificación, a los 7 caracteres: [1\]$1=
. Tenga en cuenta que esta técnica de clasificación tiene propósitos útiles, como imponer límites a un número (por ejemplo, `[[\ \ 100] $ 1 =)
Aquí está el ganador, con solo 3 caracteres:.! +
Si queremos tener el incremento y la multiplicación en el mismo bloque, debemos iterar sobre cada elemento de la matriz. Como no estamos construyendo una matriz, esto significa que deberíamos estar usando {)*}/
, lo que nos lleva a la implementación de factorial más corta de golfscript. Con 12 caracteres de largo, esto está relacionado con J!
{,1\{)*}/}:f
Soluciones de bonificación
Comenzando con una if
solución sencilla para un do
bucle:
{.{1\{.@*\(.}do;}{)}if}:f
Podemos exprimir un par extra de esto. Un poco complicado, por lo que tendrás que convencerte de que estos funcionan. Asegúrate de entender todo esto.
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
Una mejor alternativa es calcular (n + 1)! / (N + 1), lo que elimina la necesidad de una if
estructura.
{).1\{.@*\(.}do;\/}:f
Pero la do
solución más corta aquí toma algunos caracteres para asignar 0 a 1, y todo lo demás para sí mismo, por lo que no necesitamos ninguna ramificación. Este tipo de optimización es extremadamente fácil de perder.
{.!+1\{.@*\(.}do;}:f
Para cualquier persona interesada, aquí se proporcionan algunas soluciones recursivas alternativas con la misma longitud que la anterior:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
* nota: en realidad no he probado muchas de las piezas de código en esta publicación, así que siéntase libre de informar si hay errores.