dc ( calculadora de escritorio ) es una calculadora multiplataforma en polaco inverso que admite aritmética de precisión arbitraria . [1] Fue escrita por Lorinda Cherry y Robert Morris en Bell Labs . [2] Es una de las utilidades de Unix más antiguas , anterior incluso a la invención del lenguaje de programación C. Al igual que otras utilidades de esa época, tiene un potente conjunto de características pero una sintaxis concisa. [3] [4] Tradicionalmente, el programa de calculadora bc (con notación infija ) se implementó sobre dc.
Este artículo proporciona algunos ejemplos en un intento de dar una idea general del lenguaje; para obtener una lista completa de comandos y sintaxis, se debe consultar la página del manual de su implementación específica.
Historia
dc es el programa en lenguaje Unix más antiguo que aún se conserva. Cuando su sede , Bell Labs, recibió un PDP-11 , dc (escrito en B ) fue el primer lenguaje que se ejecutó en la nueva computadora, incluso antes que un ensamblador. [2] Ken Thompson ha opinado que dc fue el primer programa escrito en la máquina. [5]
Operaciones básicas
Para multiplicar cuatro y cinco en dc (tenga en cuenta que la mayoría de los espacios en blanco son opcionales):
$ gato << EOF > cal.txt
4 5 *
p
EOF
$ dc cal.txt
20
$
Los resultados también están disponibles desde los comandos:
$ echo "4 5 * p" | dc
o
$ dc -
4 5*pq
20
$ dc
4 5 *
p
20
q
$ dc -e '45*p'
Esto se traduce como "insertar cuatro y cinco en la pila, luego, con el operador de multiplicación, sacar dos elementos de la pila, multiplicarlos y colocar el resultado en la pila". Luego, el pcomando se utiliza para examinar (imprimir en la pantalla) el elemento superior de la pila. El qcomando abandona la instancia invocada de dc. Tenga en cuenta que los números deben estar espaciados entre sí, aunque algunos operadores no necesitan estarlo.
La precisión aritmética se modifica con el comando k, que establece la cantidad de dígitos fraccionarios (la cantidad de dígitos que siguen al punto ) que se utilizarán para las operaciones aritméticas. Como la precisión predeterminada es cero, esta secuencia de comandos produce 0como resultado:
2 3 / p
Si se ajusta la precisión con k, se puede generar una cantidad arbitraria de decimales. Esta secuencia de comandos genera .66666.
5 mil 2 3 / p
Para evaluar : ( calcula la raíz cuadrada de la parte superior de la pila y se utiliza para ingresar un número negativo):
v_
12 _3 4 ^ + 11 / v 22 - pag
Para intercambiar los dos elementos superiores de la pila, utilice el rcomando. Para duplicar el elemento superior, utilice el dcomando.
Entrada/salida
Para leer una línea desde la entrada estándar , utilice el ?comando . Esto evalúa la línea como si fuera un comando dc, por lo que es necesario que sea sintácticamente correcto y presenta un problema de seguridad potencial porque el !comando dc permite la ejecución arbitraria de comandos.
Como se mencionó anteriormente, pimprime la parte superior de la pila con una nueva línea después. nsaca la parte superior de la pila y la imprime sin una nueva línea final. fimprime la pila completa con una entrada por línea.
dc también admite bases de entrada y salida arbitrarias . El icomando saca la parte superior de la pila y la usa como base de entrada. Los dígitos hexadecimales deben estar en mayúsculas para evitar colisiones con los comandos dc y están limitados a AF. El ocomando hace lo mismo para la base de salida, pero tenga en cuenta que la base de entrada afecta el análisis de cada valor numérico posterior, por lo que generalmente es recomendable establecer primero la base de salida. Por lo tanto, 10oestablece la base de salida en la base de entrada actual, pero generalmente no en 10 (diez). Sin embargo, Aorestablece la base de salida en 10 (diez), independientemente de la base de entrada. Para leer los valores, los comandos Ky empujan la precisión actual, la base de entrada y la base de salida en la parte superior de la pila
I.O
A modo de ejemplo, para convertir de hexadecimal a binario:
$ echo 16i2o DEADBEEFp | dc
11011110101011011011111011101111
Características del lenguaje
Registros
Además de estas operaciones aritméticas y de pila básicas, dc incluye soporte para macros , condicionales y almacenamiento de resultados para su posterior recuperación.
El mecanismo subyacente a las macros y los condicionales es el registro , que en DC es una ubicación de almacenamiento con un nombre de carácter único que se puede almacenar y recuperar: scextrae la parte superior de la pila y la almacena en el registro c, y lcempuja el valor del registro c hacia la pila. Por ejemplo:
3 pb 4 pb * p
Los registros también pueden tratarse como pilas secundarias, por lo que se pueden insertar y extraer valores entre ellos y la pila principal mediante los comandos Sy L.
Instrumentos de cuerda
Los valores de cadena se encierran entre [caracteres ]y se pueden colocar en la pila y almacenar en registros. El acomando convierte el byte de orden inferior del valor numérico en un carácter ASCII o, si la parte superior de la pila es una cadena, lo reemplaza por el primer carácter de la cadena. No hay formas de crear cadenas ni de realizar manipulaciones de cadenas que no sean ejecutándolas con el xcomando o imprimiéndolas con el Pcomando.
El #personaje comienza un comentario hasta el final de la línea.
Macros
Las macros se implementan permitiendo que los registros y las entradas de la pila sean cadenas además de números. Una cadena se puede imprimir, pero también se puede ejecutar (es decir, procesar como una secuencia de comandos dc). Por ejemplo, podemos almacenar una macro para sumar uno y luego multiplicar por 2 en el registro m:
[1 + 2 *] pequeño
y luego (usando el xcomando que ejecuta la parte superior de la pila) podemos usarlo así:
3 lm de experiencia
Condicionales
Por último, podemos utilizar este mecanismo de macro para proporcionar condicionales. El comando =rextrae dos valores de la pila y ejecuta la macro almacenada en el registro rsolo si son iguales. Por lo tanto, esto imprime la cadena equalsolo si los dos valores superiores de la pila tienen el mismo valor:
[[igual]p] sm 5 5 =m
Otros condicionales son >, !>, <, !<, !=, que ejecutan la macro especificada si los dos valores superiores en la pila son mayor, menor o igual a ("no mayor"), menor que, mayor o igual a ("no menor que") y no igual, respectivamente. Tenga en cuenta que el orden de los operandos en las comparaciones de desigualdades es el opuesto al orden para la aritmética; 5 3 -evalúa a 5 - 3 = 2, pero ejecuta el contenido del registro porque .
5 3 <tt3 < 5
Bucles
El bucle es entonces posible definiendo una macro que (condicionalmente) se vuelve a invocar a sí misma. Un factorial simple de la parte superior de la pila podría implementarse de la siguiente manera:
# F(x): devuelve x! # si x-1 > 1 # devolver x * F(x-1) # de lo contrario # devolver x [d1-d1<F*]dsFxp
El 1Qcomando sale de una macro, lo que permite un retorno anticipado. qsale de dos niveles de macros (y de dc mismo si hay menos de dos niveles en la pila de llamadas). zempuja la profundidad de pila actual antes de la zoperación.
Ejemplos
Sumando toda la pila
Esto se implementa con una macro almacenada en un registro aque se llama a sí misma condicionalmente y realiza una suma cada vez, hasta que solo queda un valor en la pila. El zoperador se utiliza para insertar en la pila la cantidad de entradas de la pila. El operador de comparación >extrae dos valores de la pila al realizar la comparación.
dc -e "1 2 4 8 16 100 0d[+z1<a]dsaxp"
Y el resultado es 131.
Sumar todas las expresiones de dc como líneas del archivo
Un número simple es una expresión dc válida, por lo que se puede utilizar para sumar un archivo donde cada línea contiene un solo número.
Esto se implementa nuevamente con una macro almacenada en un registro aque se llama a sí misma condicionalmente, realizando una adición cada vez, hasta que solo quede un valor en la pila.
dc -e "0d[?+z1<a]dsaxp" < archivo
El ?operador lee otro comando del flujo de entrada. Si la línea de entrada contiene un número decimal, ese valor se agrega a la pila. Cuando el archivo de entrada llega al final del archivo, el comando es nulo y no se agrega ningún valor a la pila.
{ echo "5" ; echo "7" ; } | dc -e "0d[?+z1<a]dsaxp"
Y el resultado es 12.
Las líneas de entrada también pueden ser comandos de CC complejos.
{ eco "3 5 *" ; eco "4 3 *" ; eco "5dd++" ; } | dc -e "0d[?+z1<a]dsaxp"
Y el resultado es 42.
Tenga en cuenta que, dado que dc admite precisión arbitraria, no hay preocupación por el desbordamiento numérico o la pérdida de precisión, sin importar cuántas líneas contenga el flujo de entrada, a diferencia de una solución similarmente concisa en AWK .
Las desventajas de esta solución son: el bucle se detiene al encontrar una línea en blanco en el flujo de entrada (técnicamente, cualquier línea de entrada que no agregue al menos un valor numérico a la pila); y, para manejar números negativos, las instancias iniciales de '-' para indicar un signo negativo deben cambiarse a '_' en el flujo de entrada, debido al signo negativo no estándar de dc. El ?operador en dc no proporciona una forma clara de distinguir entre leer una línea en blanco y leer el final del archivo.
Conversión de unidades
Como ejemplo de un programa relativamente simple en DC, este comando (en 1 línea):
dc -e '[[Ingrese un número (metros), o 0 para salir]PAP]sh[q]sz[lhx?d0=zAk.0254/.5+0kC~1/rn[ pies ]Pn[ pulgadas]PAPdx]dx'
convierte distancias de metros a pies y pulgadas; la mayor parte del trabajo se centra en solicitar entradas, imprimir la salida en un formato adecuado y realizar un bucle para convertir otro número.
Máximo común divisor
A modo de ejemplo, a continuación se muestra una implementación del algoritmo euclidiano para encontrar el MCD :
dc -e '??[dSarLa%d0<a]dsax+p' # más corto
dc -e '[a=]P?[b=]P?[dSarLa%d0<a]dsax+[GCD:]Pp' # versión más fácil de leer
Factorial
Calcular el factorial de un valor de entrada,
dc -e '?[q]sQ[d1=Qd1-lFx*]dsFxp'
Quines en dc
También existen quines en el lenguaje de programación dc; programas que producen su código fuente como salida.
dc -e '[91Pn[dx]93Pn]dx'
dc -e '[91PP93P[dx]P]dx'
Impresión de todos los números primos
dc -e '2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l#x0<&2+lx]ds.x'
Este programa fue escrito por Michel Charpentier. Muestra la secuencia de números primos. Tenga en cuenta que es posible una implementación más corta, que lee catorce símbolos menos.
dc -e '2p3p[pq]s$[l!2+ds!l^<$dl!%0<#]s#[+dvs^1s!l#x2l.x]ds.x'
Factorización de números enteros
dc -e '[n=]P?[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2'
Este programa también fue escrito por Michel Charpentier. [6]
Hay una más corta
dc -e "[n=]P?[lfp/dlf%0=Fdvsr]sF[dsf]sJdvsr2sf[dlf%0=Flfdd2%+1+sflr<Jd1<M]dsMx"
y una solución más rápida (pruebe con el número de 200 bits 2 200 -1 (entrada 2 200^1-)
dc -e "[n=]P?[lfp/dlf% 0=Fdvsr]sFdvsr2sfd2%0=F3sfd3%0=F5sf[dlf%0=Flfd4+sflr>M]sN[dlf%0=Flfd2+sflr>N]dsMx[p]sMd1<M"
Tenga en cuenta que esto último se puede acelerar aún más si el acceso a una constante se reemplaza por un acceso a un registro.
dc -e "[n=]P?[lfp/dlf%l0=Fdvsr]sF2s2dvsr2sf4s4d2%0=F3sfd3%0=F5sf[dlf%l0=Flfdl4+sflr>M]sN[dlf%l0=Flfdl2+sflr>N]dsMx[p]sMd1<M"
Calculando Pi
Una implementación del algoritmo de Chudnovsky en el lenguaje de programación dc. El programa imprimirá aproximaciones cada vez mejores a medida que se ejecuta. Pero como pi es un número trascendental, el programa continuará hasta que se interrumpa o se agoten los recursos de la máquina en la que se ejecuta.
dc -e '_640320[0ksslk3^16lkd12+sk*-lm*lhd1+sh3^/smlxlj*sxll545140134+dsllm*lxlnk/ls+dls!=P]sP3^sj7sn[6sk1ddshsxsm13591409dsllPx10005v426880*ls/K3-k1/pcln14+snlMx]dsMx'
Una implementación rápida de la misma fórmula que duplica su tamaño en cada iteración. Evalúa un número finito si se suma como un número racional exacto y solo realiza una gran división y raíz cuadrada por iteración. Es rápida, pero se ralentizará rápidamente a medida que aumenta el tamaño de la fracción.
dc -e '1Sk1SR13591409dSBSP426880dSQ4/3^9*SC[0r-]s-[lkE*1-k10005vlQ*lP/nAan0k]dSox[Lkd1+Skdd1+Sk3^lC*SQ2*1-d3*d*4-*dSR545140134LB+dSB*lk2%0=-SP]dszx[LRLRdLP*LPLQdLQ*SQ*+SP*SR]sc[d1-d0<yd0<yd0=z0=zlcx]sy0[lcxlox1+lyxllx]dslx'
Intercambio de claves Diffie-Hellman
Un ejemplo más complejo del uso de dc integrado en un script de Perl realiza un intercambio de claves Diffie-Hellman . Este fue un bloque de firma popular entre los ciberpunks durante los debates de ITAR , donde el script corto podía ejecutarse únicamente con Perl y dc, programas omnipresentes en sistemas operativos tipo Unix: [7]
#!/usr/bin/perl -- -exportar-una-firma-de-sistema-criptográfico Diffie-Hellman-2-líneas
( $g , $e , $m ) = @ARGV , $m || die "$0 gen exp mod\n" ; print `echo "16dio1[d2%Sa2/d0<X+d*La1=z\U$m%0]SX$e"[$g*]\EszlXx+p | dc`
Una versión comentada es un poco más fácil de entender y muestra cómo usar bucles, condicionales y el qcomando para regresar de una macro. Con la versión GNU de dc, el |comando se puede usar para hacer exponenciación modular de precisión arbitraria sin necesidad de escribir la función X.
#!/usr/bin/perl
mi ( $g , $e , $m ) = map { "\U$_" } @ARGV ; morir "$0 gen exp mod\n" a menos que $m ;
print `echo $g $e $m | dc -e ' # Entrada y salida hexadecimal 16dio # Leer m, e y g desde la entrada estándar en una línea ?SmSeSg
# Función z: retorna g * parte superior de la pila
[lg*]sz
# Función Q: elimina la parte superior de la pila y devuelve 1
[sb1q]sQ
# Función X(e): calcula recursivamente g^e % m
# Es lo mismo que Sm^Lm%, pero maneja exponentes arbitrariamente grandes.
# Pila en la entrada: e
# Pila en la salida: g^e % m
# Dado que e puede ser muy grande, esto usa la propiedad que g^e % m ==
# if( e == 0 )
# devuelve 1
# x = (g^(e/2)) ^ 2
# if( e % 2 == 1 )
# x *= g
# devuelve x %
[
d 0=Q # devuelve 1 si e==0 (de lo contrario, pila: e)
d 2% Sa # Almacena e%2 en una (pila: e)
2/ # calcula e/2
lXx # llama a X(e/2)
d* # calcula X(e/2)^2
La1=z # multiplica por g si e%2==1
lm % # calcula (g^e) % m
] SX
le # Cargar e desde el registro
lXx # calcular g^e % m
p # Imprimir el resultado
'` ;
Variables de entorno
Si la variable de entorno DC_LINE_LENGTH existe y contiene un entero mayor que 1 y menor que , la salida de dígitos numéricos (según la base de salida) se restringirá a este valor, insertando a continuación barras invertidas y saltos de línea. La longitud de línea predeterminada es 70. El valor especial 0 desactiva los saltos de línea.
Véase también
- bc (lenguaje de programación)
- Métodos de entrada de la calculadora
- Calculadoras HP
- Máquina apiladora
- Notación polaca inversa
Referencias
- ^ : una calculadora de precisión arbitraria – Manual de comandos de usuario de Linux
- ^ ab McIlroy, MD (1987). Un lector de Unix para investigación: extractos anotados del Manual del programador, 1971–1986 (PDF) (Informe técnico). CSTR. Bell Labs. 139.
- ^ "Fuentes de la página del manual de Unix dc de la séptima edición". Archivado desde el original el 23 de agosto de 2004. Consultado el 23 de junio de 2004 .
- ^ Ritchie, Dennis M. (septiembre de 1979). "La evolución del sistema de tiempo compartido Unix". Archivado desde el original el 6 de mayo de 2010.
- ^ Brian Kernighan y Ken Thompson. Un deleite nerd para cualquier asistente al Vintage Computer Fest 2019: Kernighan entrevistando a Thompson sobre Unix. YouTube. El evento ocurre a los 29m45s . Consultado el 3 de septiembre de 2019 .
- ^ "Guía avanzada de scripts en Bash, capítulo 16, ejemplo 16-52 (factorización)" . Consultado el 20 de septiembre de 2020 .
- ^ Adam Back. «Diffie–Hellman en dos líneas de Perl» . Consultado el 5 de enero de 2009 .
Enlaces externos
- Paquete dc en los repositorios de Debian GNU/Linux
- – Manual de comandos generales de Linux
- – Manual del programador de Plan 9 , volumen 1
- Puerto nativo de Windows de bc , que incluye dc.