dc ( calculadora de escritorio ) es una calculadora de pulido inverso multiplataforma que admite aritmética de precisión arbitraria . [1] Escrito por Robert Morris , mientras que en los Laboratorios Bell , [2] que es una de las más antiguas de Unix servicios públicos, anteriores incluso la invención del lenguaje de programación C . Al igual que otras utilidades de esa época, tiene un poderoso conjunto de características pero una sintaxis concisa. [3] [4] Tradicionalmente, el programa de calculadora bc (con notación infija ) se implementó sobre dc.
Autor (es) original (es) | Robert Morris ( Laboratorios Bell de AT&T ) |
---|---|
Desarrollador (es) | Varios desarrolladores comerciales y de código abierto |
Escrito en | B |
Sistema operativo | Unix , similar a Unix , Plan 9 |
Plataforma | Multiplataforma |
Tipo | Mando |
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 de manual para conocer la implementación específica.
Historia
dc es el lenguaje Unix más antiguo que se conserva . Cuando su hogar Bell Labs recibió un PDP-11 , dc, escrito en B, fue el primer idioma que se ejecutó en la nueva computadora, incluso antes que un ensamblador. [5] Ken Thompson ha opinado que dc fue el primer programa escrito en la máquina. [2]
Operaciones básicas
Para multiplicar cuatro y cinco en cd (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 $
También puede obtener el resultado con los comandos:
$ echo "4 5 * p" | corriente continua
o
$ dc - 4 5 * pq 20$ dc 4 5 * p 20 q$ dc -e '4 5 * p'
Esto se traduce en "empujar cuatro y cinco a la pila, luego, con el operador de multiplicación, sacar dos elementos de la pila, multiplicarlos y devolver el resultado a la pila". Luego, el p
comando se usa para examinar (imprimir en la pantalla) el elemento superior de la pila. El q
comando cierra la instancia invocada de dc. Tenga en cuenta que los números deben estar espaciados entre sí, aunque no es necesario que algunos operadores lo estén.
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 0
como resultado:
2 3 / p
Al ajustar la precisión con k
, se puede producir un número arbitrario de lugares decimales. Esta secuencia de comandos genera salidas .66666
.
5 k2 3 / p
Para evaluar : ( v
calcula la raíz cuadrada de la parte superior de la pila y _
se usa para ingresar un número negativo):
12 _3 4 ^ + 11 / v 22 -pag
Para intercambiar los dos elementos superiores de la pila, use el r
comando. Para duplicar el elemento superior, use el d
comando.
De entrada y salida
Para leer una línea de stdin , use el ?
comando. Esto evaluará la línea como si fuera un comando dc, por lo que es necesario que sea sintácticamente correcto y potencialmente sea un problema de seguridad ya que el !
comando dc permitirá la ejecución arbitraria del comando.
Como se mencionó anteriormente, p
imprimirá la parte superior de la pila con una nueva línea después. n
aparecerá en la parte superior de la pila y lo mostrará sin una nueva línea al final. f
volcará toda la pila con una entrada por línea.
dc también soporta entrada y de salida arbitrarias radices . El i
comando aparecerá en la parte superior de la pila y lo usará como base de entrada. Los dígitos hexadecimales deben estar en mayúsculas para evitar colisiones con los comandos de CC y están limitados a AF. El o
comando hace lo mismo para la base de salida, pero tenga en cuenta que la base de entrada afectará el análisis de cada valor numérico posteriormente, por lo que generalmente es aconsejable establecer la base de salida primero. Por lo tanto, 10o
establece la base de salida en la base de entrada actual, pero generalmente no en 10 (diez). No obstante, Ao
restablece la base de salida a 10 (diez), independientemente de la base de entrada. Para leer los valores, los comandos K
, I
y O
empujarán 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 | CC 11011110101011011011111011101111
Caracterí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 de: sc
abre la parte superior de la pila y la almacena en el registro c, y lc
empuja el valor del registro c en la pila. Por ejemplo:
3 mp 4 lc * p
Los registros también se pueden tratar como pilas secundarias, por lo que los valores se pueden insertar y colocar entre ellos y la pila principal mediante los comandos S
y L
.
Instrumentos de cuerda
Los valores de cadena se encierran [
y ]
caracteres y pueden ser empujados en la pila y se almacenan en los registros. El a
comando convertirá 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, la reemplazará con el primer carácter de la cadena. No hay formas de crear cadenas o realizar manipulación de cadenas que no sea ejecutarlas con el x
comando o imprimirlas con el P
comando.
El #
personaje comienza un comentario hasta el final de la línea.
Macros
Luego, las macros se implementan permitiendo que los registros y las entradas de la pila sean cadenas y números. Se puede imprimir una cadena, pero también se puede ejecutar (es decir, procesarse como una secuencia de comandos de CC). Entonces, por ejemplo, podemos almacenar una macro para agregar una y luego multiplicar por 2 en el registro m:
[1 + 2 *] sm
y luego (usando el x
comando que ejecuta la parte superior de la pila) podemos usarlo así:
3 lm xp
Condicionales
Finalmente, podemos utilizar este macro mecanismo para proporcionar condicionales. El comando =r
sacará dos valores de la pila y ejecutará la macro almacenada en el registro r
solo si son iguales. Entonces esto imprimirá la cadena equal
solo si la parte superior de la pila es igual a 5:
[[igual] p] sm 5 = m
Otros condicionales son >
, !>
, <
, !<
, !=
, que ejecutará la macro especificada si los dos valores superiores en la pila son mayores, menor o igual a ( "no superior"), menor que, mayor que o igual a ( "no menos de ") y no iguales, respectivamente.
Bucles
Entonces, es posible realizar un bucle 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 como:
# F (x): devuelve x!# si x-1> 1# return x * F (x-1)# de lo contrario# return x[d1-d1
El 1Q
comando saldrá de una macro, lo que permitirá un regreso anticipado. q
saldrá de dos niveles de macros (y el mismo dc si hay menos de dos niveles en la pila de llamadas). z
empujará la profundidad de pila actual antes de la z
operación.
Ejemplos de
Sumando toda la pila
Esto se implementa con una macro almacenada en el registro a
que se llama condicionalmente a sí misma, realizando una adición cada vez, hasta que solo queda un valor en la pila. El z
operador se utiliza para empujar el número de entradas de la pila a la pila. El operador de comparación >
saca dos valores de la pila al hacer la comparación.
dc -e "1 2 4 8 16100 0d [+ 2z> a] salaxp"
Y el resultado es 131.
Sumando todas las expresiones dc como líneas del archivo
Un número simple es una expresión de cd 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 el registro a
que se llama condicionalmente a sí misma, realizando una adición cada vez, hasta que solo queda un valor en la pila.
archivo cat | dc -e "0d [? + 2z> a] salaxp"
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 [? + 2z> a] salaxp"
Y el resultado es 12.
Las líneas de entrada también pueden ser comandos de CC complejos.
{ echo "3 5 *" ; echo "4 3 *" ; echo "5dd ++" ; } | dc -e "0d [? + 2z> a] salaxp"
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 concisa similar en AWK .
Las desventajas de esta solución son: el bucle cesará 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 entre la lectura de una línea en blanco y 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] psj] sh [q] sz [lhx? d0 = z10k39.370079 * .5 + 0k12 ~ 1 / rn [pies] Pn [pulgadas] P10Pdx ] dx '
convertirá distancias de metros a pies y pulgadas; la mayor parte tiene que ver con solicitar la entrada, imprimir la salida en un formato adecuado y recorrer para convertir otro número.
Máximo común divisor
Como ejemplo, aquí hay una implementación del algoritmo euclidiano para encontrar el GCD :
dc -e '?? [dSarLa% d0 ]> #
dc más corto -e '[a =] P? [b =] P? [dSarLa% d0 ]> # 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'
Imprimir todos los números primos
echo '2p3p [dl! d2 + s!% 0 = @ l! l ^! <#] s # [s / 0ds ^] s @ [p] s & [ddvs ^ 3s! l # x0 <& 2 + lx] ds .x ' | corriente continua
Este programa fue escrito por Michel Charpentier. Da salida a la secuencia de números primos. Tenga en cuenta que puede acortarse con un símbolo, que parece ser la solución mínima.
echo '2p3p [dl! d2 + s!% 0 = @ l! l ^! <#] s # [0 * ds ^] s @ [p] s & [ddvs ^ 3s! l # x0 <& 2 + lx] ds .x ' | corriente continua
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. [6]
Hay un mas corto
dc -e "[n =] P? [lfp / dlf% 0 = Fdvsr] sF [dsf] sJdvsr2sf [dlf% 0 = Flfdd2% + 1 + sflr
y una solución más rápida (pruebe con el número de 200 bits 2200-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
Tenga en cuenta que este último puede incluso acelerarse, si el acceso a una constante se reemplaza por un registro de acceso.
dc -e "[n =] P? [lfp / dlf% l0 = Fdvsr] sF2s2dvsr2sf4s4d2% 0 = F3sfd3% 0 = F5sf [dlf% l0 = Flfdl4 + sflr> M] sN [dlf% l0 = Flfdl2 + sflr ] dsMx [p] sMd1
Intercambio de claves Diffie-Hellman
Un ejemplo más complejo de uso de CC incrustado en un script Perl realiza un intercambio de claves Diffie-Hellman . Esto fue popular como un bloque de firmas entre cypherpunks durante los debates de ITAR , donde el script corto se podía ejecutar solo con Perl y dc, programas ubicuos en sistemas operativos similares a Unix: [7]
#! / usr / bin / perl - -export-a-crypto-system-sig Diffie-Hellman-2-lines ( $ g , $ e , $ m ) = @ARGV , $ m || morir "$ 0 gen exp mod \ n" ; print `echo" 16dio1 [d2% Sa2 / d0 dc`
Una versión comentada es un poco más fácil de entender y muestra cómo usar bucles, condicionales y el q
comando para regresar de una macro. Con la versión GNU de dc, el |
comando se puede usar para hacer una exponenciación modular de precisión arbitraria sin necesidad de escribir la función X.
#! / usr / bin / perlmi ( $ g , $ e , $ m ) = mapa { "\ U $ _" } @ARGV ; muere "$ 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 de stdin en una línea ?# Función z: return g * top of stack [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. # Apilar en la entrada: e # Apilar en la salida: g ^ e% m # Dado que e puede ser muy grande, esto usa la propiedad de que g ^ e% m == # if (e == 0) # return 1 # x = (g ^ (e / 2)) ^ 2 # if (e% 2 == 1) # x * = g # return x% [ d 0 = Q # return 1 if e == 0 (de lo contrario, apile: e) d 2% Sa # Almacenar e% 2 en una (pila: e) 2 / # calcular e / 2 lXx # llamar a X (e / 2) d * # calcular X (e / 2) ^ 2 La1 = z # multiplicar por g si e% 2 == 1 lm% # calcular (g ^ e)% m ] SXle # Cargar e desde el registro lXx # calcular g ^ e% m p # Imprimir el resultado '' ;
Ver también
- bc (lenguaje de programación)
- Métodos de entrada de calculadora
- Calculadoras HP
- Máquina de apilar
Referencias
- ^ : una calculadora de precisión arbitraria - Manual de comandos de usuario de Linux
- ^ a b Brian Kernighan y Ken Thompson. Una delicia nerd para cualquier asistente de Vintage Computer Fest 2019: Kernighan entrevistando a Thompson sobre Unix . YouTube. El evento ocurre a las 29m45s . Consultado el 3 de septiembre de 2019 .
- ^ "Las fuentes de la página de manual de la 7ª edición de Unix dc" .
- ^ 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.
- ^ McIlroy, MD (1987). Un lector de investigación Unix: extractos comentados del Manual del programador, 1971–1986 (PDF) (Informe técnico). CSTR. Bell Labs. 139.
- ^ "Guía avanzada de Bash-Scripting, 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 repositorios de Debian GNU / Linux
- - Manual del programador de Plan 9 , Volumen 1
- Puerto nativo de Windows de bc , que incluye dc.
- dc incrustado en una página web