dc ( calculadora de escritorio ) es una calculadora polaca inversa multiplataforma que admite aritmética de precisión arbitraria . [ 1 ] Fue escrita por Lorinda Cherry y Robert Morris en Bell Labs . [ 2 ] dc es una de las utilidades Unix más antiguas , anterior incluso al desarrollo 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 ] Aunque el programa de calculadora bc (que usa notación infija ) se implementó tradicionalmente sobre dc, la implementación moderna de dc de GNU se basa en bc. [ 5 ]
Historia
dc es el programa en lenguaje Unix más antiguo que aún se conserva. Cuando Bell Labs , donde se desarrolló dc, recibió una PDP-11 , dc —escrito en B— fue el primer programa 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. [ 6 ]
Operaciones básicas
Para multiplicar cuatro y cinco en dc (tenga en cuenta que la mayor parte del espacio en blanco es opcional):
$ cat << EOF > cal.txt 4 5 * p EOF$ dc cal.txt 20 $Los resultados también están disponibles mediante los siguientes comandos:
$ echo "4 5 * p" | dc o
$ dc - 4 5*pq 20$ dc 4 5 * p 20 q$ dc -e '4 5 * p'Esto se traduce en "insertar cuatro y cinco en la pila, luego, con el operador de multiplicación, extraer dos elementos de la pila, multiplicarlos e insertar el resultado en la pila". A continuación, pse utiliza el comando para examinar (imprimir en pantalla) el elemento superior de la pila. El qcomando finaliza la instancia de dc invocada. Tenga en cuenta que los números deben estar separados entre sí, aunque algunos operadores no lo requieran.
La precisión aritmética se cambia con el comando k, que establece el número de dígitos fraccionarios (el número de dígitos que siguen al punto ) que se utilizarán para las operaciones aritméticas. Dado que la precisión predeterminada es cero, esta secuencia de comandos produce 0como resultado:
2 3 / p
Ajustando la precisión con k, se puede producir un número arbitrario de decimales. Esta secuencia de comandos produce .66666.
5 k 2 3 / p
Para evaluar: ( vcalcula la raíz cuadrada del elemento superior de la pila y _se utiliza para introducir un número negativo):
12 _3 4 ^ + 11 / v 22 - pag
Para intercambiar los dos elementos superiores de la pila, use el rcomando. Para duplicar el elemento superior, use el dcomando.
Entrada/salida
Para leer una línea desde stdin , utilice el ?comando. Este comando evalúa la línea como si fuera un comando dc, por lo que es necesario que sea sintácticamente correcto y presenta un posible problema de seguridad, ya que el !comando dc permite la ejecución de comandos arbitrarios.
Como se mencionó anteriormente, pimprime la parte superior de la pila con un salto de línea después. nExtrae la parte superior de la pila y la imprime sin un salto de línea final. fImprime toda la pila con una entrada por línea.
dc también admite bases de entrada y salida arbitrarias . El icomando extrae la parte superior de la pila y la usa para la 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 a la base de entrada actual, pero generalmente no a 10 (diez). Sin embargo, Aorestablece la base de salida a 10 (diez), independientemente de la base de entrada. Para leer los valores, los comandos K, Iy Ocolocan la precisión actual, la base de entrada y la base de salida en la parte superior de la pila.
Como ejemplo, para convertir de hexadecimal a binario:
$ echo 16i2o DEADBEEFp | dc 11011110101011011011111011101111Características del idioma
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 condicionales es el registro , que en dc es una ubicación de almacenamiento con un nombre de un solo carácter que se puede almacenar y recuperar: scextrae la parte superior de la pila y la almacena en el registro c, y lccoloca el valor del registro c en la pila. Por ejemplo:
3 sc 4 lc * p
Los registros también pueden tratarse como pilas secundarias, por lo que los valores pueden insertarse y extraerse entre ellos y la pila principal utilizando los comandos Sy .L
Instrumentos de cuerda
Los valores de cadena se encierran entre [caracteres ]y pueden colocarse en la pila y almacenarse en registros. El acomando convierte el byte de menor orden del valor numérico en un carácter ASCII , o si la parte superior de la pila es una cadena, la reemplaza con el primer carácter de la cadena. No hay forma de construir cadenas ni de manipularlas, salvo 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 tanto cadenas como 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 *] sm
y luego (usando el xcomando que ejecuta la parte superior de la pila) podemos usarlo así:
3 lm xp
Condicionales
Finalmente, podemos usar este mecanismo de macros 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] sr 5 5 =r
Otras condiciones son >, !>, <, !<, !=, que ejecutan la macro especificada si los dos valores superiores de la pila son mayores, menores o iguales que ("no mayor que"), menores que, mayores o iguales que ("no menor que") y distintos de, respectivamente. Nótese que el orden de los operandos en las comparaciones de desigualdad es el opuesto al orden para la aritmética; 5 3 -se evalúa como 5 - 3 = 2, pero ejecuta el contenido del registro porque .5 3 <tt3 < 5
Bucles
El bucle se puede crear definiendo una macro que se reinicie (condicionalmente). Un factorial simple del elemento 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 del propio dc si hay menos de dos niveles en la pila de llamadas). zEmpuja la profundidad actual de la pila 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, realizando una suma cada vez, hasta que solo queda un valor en la pila. El zoperador se utiliza para insertar en la pila el número de entradas que hay en ella. 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.
Sumando todas las expresiones dc como líneas del archivo
Un número simple es una expresión DC válida, por lo que se puede usar 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 de forma condicional, realizando una suma cada vez, hasta que solo queda 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, 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 dc complejos.
{ echo "3 5 *" ; echo "4 3 *" ; echo "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 que preocuparse por el desbordamiento numérico o la pérdida de precisión, independientemente de la cantidad de líneas que contenga el flujo de entrada, a diferencia de una solución igualmente 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 denotar 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 la lectura de una línea en blanco de la lectura del 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 programa se dedica a solicitar la entrada de datos, imprimir la salida en un formato adecuado y repetir el proceso para convertir otro número.
Máximo común divisor
Como ejemplo, aquí se muestra una implementación del algoritmo euclidiano para hallar el MCD :
dc -e '??[dSarLa%d0<a]dsax+p' # versión más corta dc -e '[a=]P?[b=]P?[dSarLa%d0<a]dsax+[GCD:]Pp' # versión más fácil de leerFactorial
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'Imprimir 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. Genera una secuencia de números primos. Cabe destacar que existe una versión más corta que requiere 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 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. [ 7 ]
Hay uno más corto
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 (intente 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"Cabe señalar 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
Implementación del algoritmo de Chudnovsky en el lenguaje de programación dc. El programa imprimirá aproximaciones cada vez mejores a medida que se ejecute. Sin embargo, dado que 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'Implementación rápida de la misma fórmula mediante el método de divide y vencerás, cuyo tamaño se duplica en cada iteración. Evalúa un número finito cuyas sumas resultan en un número racional exacto y realiza una única división y raíz cuadrada por iteración. Es rápida, pero su velocidad disminuye 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 . Esto fue popular como bloque de firma entre los cypherpunks durante los debates de ITAR , donde el breve script podía ejecutarse solo con Perl y dc, programas omnipresentes en sistemas operativos tipo Unix: [ 8 ]
#!/usr/bin/env perl -- -export-a-crypto-system-sig Diffie-Hellman-2-lines ( $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 retornar desde una macro. Con la versión GNU de dc, el |comando se puede usar para realizar exponenciaciones modulares de precisión arbitraria sin necesidad de escribir la función X.
#!/usr/bin/env perlmi ( $g , $e , $m ) = map { "\U$_" } @ARGV ; die "$0 gen exp mod\n" unless $m ;print `echo $g $e $m | dc -e ' # Entrada y salida hexadecimal 16dio # Leer m, e y g desde stdin en una línea ?SmSeSg# Función z: devuelve g * cima 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 de que g^e % m == # si( e == 0 ) # devuelve 1 # x = (g^(e/2)) ^ 2 # si( 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 a (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 ] SXle # Cargar e del registro lXx # calcular g^e % m p # Imprimir el resultado '` ;Variables ambientales
Si la variable de entorno DC_LINE_LENGTH existe y contiene un número entero mayor que 1 y menor queLa salida de dígitos numéricos (según la base de salida) se limitará a este valor, insertando posteriormente 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
Referencias
- ↑ : una calculadora de precisión arbitraria – Manual de comandos de usuario de Linux
- 1 2 McIlroy, MD (1987). Un lector de Research Unix: extractos anotados del Manual del programador, 1971–1986 (PDF) (Informe técnico). CSTR. Bell Labs. 139.
- ↑ "Fuentes para la página del manual de la 7.ª edición de Unix dc" . Archivado del 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 del original el 6 de mayo de 2010.
- ↑ "GNU bc" . Consultado el 25 de enero de 2025 .
- ↑ Brian Kernighan y Ken Thompson. Una delicia para los amantes de la informática que asistirán al Vintage Computer Fest 2019: Kernighan entrevista a Thompson sobre Unix . YouTube. El evento comienza a los 29 minutos y 45 segundos . Consultado el 3 de septiembre de 2019 .
- ↑ "Guía avanzada de scripting Bash, capítulo 16, ejemplo 16-52 (factorización)" . Consultado el 20 de septiembre de 2020 .
- ↑ Adam Back. "Diffie–Hellman en 2 líneas de Perl" . Consultado el 5 de enero de 2009 .
Enlaces externos
- Paquete dc en los repositorios de Debian GNU/Linux
- Paquete más reciente GNU bc-1.08.1 que incluye dc.
- – Manual de comandos generales de Linux de ManKier.com
- – Manual del programador de Plan 9 , Volumen 1
- Versión nativa para Windows de bc , que incluye dc.
- Software multiplataforma
- Software Unix
- Calculadoras de software
- Software matemático gratuito
- Lenguajes de programación numérica
- Lenguajes de programación orientados a pilas
- Órdenes del Plan 9