Consejos para jugar golf en dc


18

¿Qué consejos generales tienes para jugar al golf en DC ?

dc es una utilidad de calculadora para UNIX / Linux que es anterior al lenguaje C. Estoy interesado en cómo acortar mis programas de CC (¿cálculos?). Estoy buscando ideas que se puedan aplicar al general que sean al menos un poco específicas de DC (por ejemplo, eliminar comentarios no es una respuesta útil)

Por favor, publique un consejo por respuesta.


77
Utiliza Marvel en su lugar.
Urna de pulpo mágico el

Respuestas:


6

Declaraciones If-then-else

Supongamos que queremos verificar la condición a==b(let ay bse almacenará en sus registros nombrados respectivamente).

editar:
[         # Everything is wrapped in one big macro
  [         # An inner macro for our *then* part
              # <-- Stuff to execute if a==b here
  2Q          # Then quit the inner and outer macro
]sE       # `E' is for Execution register ;)
la lb =E  # if a==b, execute E
          # if E is executed, it will quit the whole macro, so the rest is never reached:
          # <-- Stuff to execute if a!=b here
]x        # End macro; Execute

Dejar (foo)ser un marcador de posición, con el fin de condensar:

[[(then)2Q]sE(condition)E(else)]x

Estoy bastante seguro de que esta es la declaración más compacta posible (también presentada aquí ).


1
Tal vez [[thenaction]P][[elseaction]P][r]sI 2 4 =I x sI fes un comienzo? Las acciones para tehn y más están en la pila, la Imacro " f" las intercambia y se llama condicionalmente. entonces se ejecutará la parte superior de la pila y la macro no utilizada se soltará en I para limpiar la pila. 2 4son solo los datos de ejemplo para comparar. Como alternativa, la [x]sIparte se puede mover a la comparación, si se considera más fácil de leer: [[thenaction]P][[elseaction]P] 4 4 [r]sI =I x sI f. En flos ejemplos solo se mostrará que la pila está limpia después ...

1
La página de Rosetta Code sobre Dc menciona 3 sabores de dcy esa fue la primera página donde vi dcla if-then-elseconstrucción de OpenBSD . Creo que necesitamos un dcpaquete de ventiladores con los 3 sabores para todos los principales sistemas operativos ... o :-) ... y mi if-then-elsepropuesta anterior no funciona en el original dcporque carece del rcomando ... :-(

1
¿Qué pasa con: [[(if)2Q]si(condition)i(else)]x- envolver todo en una macro, y la porción if dentro de otra macro dentro de eso, para que pueda 2Qsalir de todo antes de llegar a la porción else. Entonces, si desea hacer si 1 == 1, imprima 1 más imprima 2 , sería 1[[1P2Q]si1=i2P]x(no probado ya que no tengo acceso a CC aquí y ahora. También estaba seguro de que había hecho este truco en una respuesta aquí antes pero no pudo encontrarlo)
daniero

Sí, hice los cálculos, mi sugerencia es más corta. Con el mismo ejemplo y "notación", y eliminando espacios en blanco es [/*else*/]sE[[/*then*/]sE]sIlalb=IlExvs [[/*then*/2Q]sIlalb=I/*else*/]x- 6 bytes de diferencia. Aún no probado: P
daniero

1
Buen trabajo, @daniero! Actualizaré la publicación cuando tenga tiempo, o puedes hacerlo si lo deseas.
Joe

5

Puede guardar la entrada con d

Al usar d, que duplica el ToS (parte superior de la pila), puede mover la entrada fuera del camino para su uso posterior, sin dejar de poder usarlo.


@NoOneIsHere oh genial !!! ¡Gracias!
Rɪᴋᴇʀ

5

Matrices

Aunque son un dolor de cabeza para los principiantes, dcofrece matrices. Funcionan así:

value index :a    # store `value' in array linked to top of stack `a', with index `index'
      index ;a    # push a[index] on (main) stack

Como de costumbre, el primer elemento tiene el índice 0. Las matrices pueden ser útiles cuando se trabaja con secuencias, como en la secuencia SUDSI , especialmente en combinación con contadores. Las matrices pueden reducir la cantidad de combinación de números que debe hacer (y la cantidad de contadores y comparaciones) si desea seleccionar un elemento en particular sin destruir su entorno. Por ejemplo, si desea mover una pila de números a una matriz, puede escribir una función recursiva que utilice z(profundidad de la pila) o z 1-como índice, almacene el elemento y compruebe si se z == 0termina.

[z 1- :a z 0 !=F]dsFx    # or I could just write such a function for you :)

Tenga en cuenta lo siguiente:

  • Las matrices están asociadas con instancias en pilas con nombre. Si inserta un nuevo valor en una pila que tiene una matriz asociada, esa matriz también será "empujada hacia atrás" y una matriz "nueva" ocupará su lugar. La matriz anterior no será utilizable hasta que el valor correspondiente en la pila nombrada también sea utilizable (es decir, en la parte superior de su pila). Este es un concepto complicado que se explicaría mejor con una buena animación, que está más allá de mí.
  • Usted puede almacenar cosas en una matriz denominada sin empujar realmente un valor en el correspondiente registro de llamada. Sin embargo, si hace esto, no puede acceder a la pila / registro con ese nombre durante el resto de la sesión. dcse estrellará
  • Si saca un valor de una pila con nombre, se perderán los valores en la matriz correspondiente, sin advertencias, sin salvaguardas, nada. Simplemente desaparecido (que también puede ser útil).

Buen trabajo con los consejos DC!
Rɪᴋᴇʀ

dcpuede haber sido actualizado recientemente, y el comportamiento de la matriz puede haber cambiado ligeramente con respecto al bloqueo. No puedo confirmar tampoco en este momento, pero creo que algo fue diferente la última vez que lo usé en Linux.
Joe

1
Si intenta leer un índice de una matriz que no se ha establecido, obtendrá 0 y no un error. Lo que puede ser muy útil, pero también vale la pena tener en cuenta si potencialmente está poniendo 0s en matrices ... Necesitará otra forma de probar que el índice ha sido tocado.
brhfl

5

0 a la enésima potencia en lugar de condicionales / macros

A veces puede que tenga algo como ternaria condicional:

A == B ? C : D;

Una buena manera de manejar esto se describe en la respuesta de @ Joe . Sin embargo, podemos hacerlo mejor:

0AB-^E*C+

donde E es D - C.

Esto prueba la igualdad elevando 0 a la potencia de la diferencia de los dos valores. Esto da como resultado 1 si es igual y 0 en caso contrario. El resto simplemente escala el 1 o 0 a los valores C o D. Esto funciona porque dcda 0 0 = 1 y 0 n = 0 para n! = 1.


4

A veces es necesario descartar un número de la pila. Una forma de hacerlo es simplemente introducirlo en una variable no utilizada, es decir st. Sin embargo, en algunas situaciones, puede abrirlo en un par de otros lugares, por ejemplo, la base de entrada cuando no tenga más entrada numérica o al especificador de precisión si no tiene más operaciones que hacer donde la precisión marcaría la diferencia. En el primer caso, use i. En el último caso, use k.


Si la salida numérica no es importante, también ose puede usar. Y si alguna de estas cosas no es importante, se pueden usar como almacenamiento y como simple descarte - I/ K/ Orecuperarlos respectivamente, y guardar bytes sobre sa/ laetc. Valores válidos AFAIK: i2-16; kcualquier número entero no negativo; ocualquier número entero mayor que 1.
brhfl

4

Cálculo de longitud: Z, Xyz

Zabre el ToS y empuja el número de dígitos (decimal) si es un número o el número de caracteres si es una cadena. Esto puede ser útil para detectar la longitud de un resultado (para la memoria intermedia) o calcular la longitud de la cadena. Tenga en cuenta que para los números, Zempuja la longitud combinada de la parte entera y la parte fracción.

Xabre el ToS y empuja el número de dígitos en la parte fraccionaria del número. Si el ToS era una cadena, 0se empuja.

Para encontrar el número de dígitos en la parte entera del número, uno podría usar dZrX-. Si no ha cambiado la precisión por defecto k==0, el uso 1/Zes más corto, pero suponga que necesita mantener una precisión particular distinta de cero después de la operación: Kr0k1/Zrkes más bien una monstruosidad.

zempuja el número de artículos en la pila. ¡Uno de mis comandos favoritos, en realidad no muestra ningún valor! Podría usarse para generar una secuencia de números o incrementar un contador. El uso zdrepetido (digamos, al comienzo de una macro) podría permitir probar un cálculo en cada número natural o entero en orden ascendente.


He usado zesto y aquello antes, pero nunca se me ocurrió usarlo como un truco de un mostrador ... Excelente ...
brhfl

4

Los dígitos Aa Fse pueden usar en sustitución de los números del 10 al 15. Sin embargo, aún deben tratarse eficazmente como dígitos de base 10 (suponiendo que la base de entrada sea 10) cuando se encuentran en diferentes lugares. En otras palabras, con la base de entrada 10 FFno representaría 255, representaría (15 * 10) + 15o 165.

De hecho esto funciona para todos los dígitos 0que Fen cualquier base de entrada 2a 16. Entonces, si la base de entrada es 5, entonces 26Esería (2 * 5^2) + (6 * 5) + 14, o 94.

Tenga en cuenta que este comportamiento está vigente para las fuentes GNU no modificadas. Sin embargo, como señala @SophiaLechner, las distribuciones basadas en RedHat parecen usar bc-1.06-dc_ibase.patch que cambia este comportamiento para que los dígitos> = ibase se traten como ibase - 1, independientemente de su valor real. Tenga en cuenta que el TIO dc parece no tener bc-1.06-dc_ibase.patch (a pesar de que es Fedora 28 ¯_ (ツ) _ / ¯).


Esto no es del todo correcto, aunque los dígitos individuales sobre la base de entrada se interpretan como cabría esperar, si el literal tiene varios dígitos, o incluso un punto decimal, los dígitos no válidos para la base se interpretan como (base-1). Entonces, en la base de entrada 10 FFrepresenta 99, en la base de entrada 5 26Ees lo mismo que 244, es decir, la base 10 74.
Sophia Lechner

@SophiaLechner ¿Estás seguro? tio.run/##S0n@/9/QIJ/L0CCTy82tgMs0k8vIzLXg/38A ¿Qué dcversión está ejecutando? Estoy usando GNU dc 1.4.1 en ubuntu y GNU dc 1.3 en MacOS
Digital Trauma

Interesante. Estoy ejecutando 1.3.95 en Red Hat, y aquí está su programa de muestra: [slechner @ XXX] $ dc -e '10o 10i FFp 5i 26Ep' 99 74 [slechner @ XXX] $ dc --version dc (GNU bc 1.06 .95) 1.3.95
Sophia Lechner

Argh ... no puede hacer que el bloque de código funcione en el comentario. El punto es que FFpsalidas 99en 1.3.95. ¿Podrías comprobar esto en tu versión de MacOS, entonces?
Sophia Lechner

1
¡Cosa segura! Gracias por toda la investigación.
Sophia Lechner

2

Al inicializar una macro de función (usaremos F) que desea ejecutar de inmediato, use algo así como en dsFxlugar de sFlFx. Lo mismo funciona para las variables: en dsalugar de sala.

Si necesita hacer otras cosas entre el almacenamiento y la carga (por ejemplo, sa[other stuff]la), considere si lo anterior es viable: si deja un valor en la pila antes de las otras operaciones, ¿volverá a estar en la parte superior al final? de esas operaciones?


2

Acabo de descubrir esto por accidente. Sin embargo, otra manera de generar un cero: _.

_es una señal a dc de que los siguientes dígitos son un número negativo. Ejemplo:

_3 # pushes -3

Pero, ¿y si no lo seguimos con un número?

_ # pushes 0...sometimes

Esto funciona cuando el siguiente carácter no en blanco después del guión bajo no es un dígito. Si un dígito lo sigue, incluso después de una nueva línea, se interpreta como un signo negativo.

c4 5_6  # -6,5,4
c4 5_ 6 # -6,5,4
c4 5_
6       # -6,5,4 # still a negative sign since the next thing it sees is a digit
c4 5_z  #  3,0,5,4 # if it's followed by a non-digit, it's a 0
c4 5_p6 #  6,0,5,4
c4 _*   #  0 # 4*0=0

1

Si el contenido de toda la pila necesita imprimirse al final de un programa, se podría utilizar un bucle macro recursivo para lograr esto. Sin embargo, es mucho más corto simplemente usar el fcomando.


1

dclee la entrada de una línea a la vez. Si necesita leer en varios elementos, hacerlo uno por línea requiere un ?para cada línea para leer, o un engorroso macro loop. En cambio, si todos los elementos de entrada pueden colocarse en una línea separada por espacios, entonces un solo ?leerá todos los elementos de entrada, empujando cada uno a la pila.

Por ejemplo en seq 10 | dc -e'?f', seqsalidas enteros 1-10, uno por línea. el ?solo leerá el primero 1que saldrá cuando se fdescargue toda la pila. Sin embargo seq 10 | tr '\n' ' ' | dc -e'?f', en trhace que la entrada integre todo el espacio separado. En este caso ?, leerá todos los enteros de la línea de una vez y los fgenerará todos.


1

Si un operador está restringido desde la fuente, cree uno nuevo con a

Algo que me ha resultado útil un par de veces ahora es evitar usar un operador específico presionando el valor ASCII del operador, usarlo apara convertirlo en una cadena y storsionarlo en un registro para ejecutarlo como macro más adelante en. Por ejemplo, necesito hacer una división, pero no puedo o intento evitar usar el personaje /. Puedo, en cambio, hacerlo 47asdy luego en el futuro cuando necesito dividir 16 por 4 16 4 ldx,.

  • Esto solo funcionará para operadores de un solo carácter (no puede construir una cadena), y no funcionará para comandos como sese que deben ser fijados por algo.
  • Esto agrega bastantes bytes y, por lo tanto, solo es adecuado cuando es necesario evitar el carácter específico o de alguna manera ofrece una bonificación de puntaje.

1

Evitar espacios en blanco

Evitar el espacio en blanco surge en bastantes desafíos, y generalmente es fácil dc. Aparte de las cadenas, la única vez muy específico que hace necesario un espacio en blanco es al empujar varios números en una fila: 1 2 3. Si esto debe evitarse:

  • Ejecutar una macro vacío en el medio: 1[]x2[]x3[]x.
  • Si los soportes están fuera de la mesa, almacenar un NOP por delante de un macro de tiempo: 35asny ejecutar que en el medio: 1lnx2lnx3lnx.

También puede separar números por comas, si está dispuesto a soportar dc: ',' (054) unimplementedadvertencias.
Trauma digital

No había pensado en eso, presumiblemente eso se aplica a cualquier token dado que no resuelve un comando ... interesante ...
brhfl
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.