Оригинальный автор (ы) | Роберт Моррис. (ATT Bell Laboratories ) |
---|---|
Разработчик (и) | Различные разработчики с открытым исходным кодом и коммерческие разработчики |
Написано на | B |
Операционной системе | Unix, Unix-подобный, Plan 9 |
Платформа | Кросс-платформенный |
Тип | Команда |
dc(настольный калькулятор) - это кроссплатформенный калькулятор с обратной полировкой, поддерживающий арифметику произвольной точности. Написанная Робертом Моррисом в Bell Labs, это одна из старейших утилит Unix, предшествующих даже изобретению языка программирования C. Как и другие утилиты того же года выпуска, она имеет мощный набор функций, но лаконичный синтаксис. Традиционно программа калькулятора bc (с инфиксной нотацией ) была реализована поверх dc.
В этой статье приводится несколько примеров, пытающихся дать общее представление о языке; для получения полного списка команд и синтаксиса следует обратиться к странице руководства для конкретной реализации.
dc - самый старый из сохранившихся языков Unix. Когда ее домашний Bell Labs получил PDP-11, dc - написанный на B - был первым языком, работающим на новом компьютере, даже до появления ассемблера.. Кен Томпсон высказал мнение, что dc была самой первой программой, написанной на машине.
Чтобы умножить четыре и пять в постоянном токе (обратите внимание, что большая часть пробел не является обязательным):
$ cat << EOF>cal.txt 4 5 * p EOF $ dc cal.txt 20 $
Вы также можете получить результат с помощью команд:
$ echo "4 5 * p" | dc
или
$ dc - 4 5 * pq 20 $ dc 4 5 * p 20 q $ dc -e '4 5 * p'
Это переводится как «поместить четыре и пять в стек, затем с помощью оператора умножения извлеките два элемента из стека, умножьте их и поместите результат обратно в стек ». Затем используется команда p
для проверки (вывода на экран) верхнего элемента в стеке. Команда q
закрывает вызванный экземпляр dc. Обратите внимание, что числа должны располагаться на расстоянии друг от друга, даже если этого не требуется для некоторых операторов.
Арифметическая точность изменяется с помощью команды k
, которая устанавливает количество цифр дробной части (количество цифр после точки ) для арифметических операций. Поскольку точность по умолчанию равна нулю, эта последовательность команд дает в результате 0
:
2 3 / p
Путем настройки точности с помощью k
, может быть произведено произвольное количество десятичных знаков. Эта последовательность команд выводит .66666
.
5 k 2 3 / p
Чтобы оценить : (v
вычисляет квадратный корень из верхней части stack, а _
используется для ввода отрицательного числа):
12 _3 4 ^ + 11 / v 22 - p
Чтобы поменять местами два верхних элемента стека используйте команду r
. Чтобы продублировать верхний элемент, используйте команду d
.
Чтобы прочитать строку из stdin, используйте команду ?
. Это будет оценивать строку, как если бы это была команда dc, поэтому необходимо, чтобы она была синтаксически правильной и потенциально представляла проблему безопасности, поскольку команда !
dc допускает выполнение произвольной команды.
Как упоминалось выше, p
будет печатать верхнюю часть стека с новой строкой после нее. n
вытянет верхнюю часть стека и выведет ее без завершающей новой строки. f
сбросит весь стек с одной записью в строке.
dc также поддерживает произвольный ввод и вывод счисления. Команда i
поднимет верхнюю часть стека и использует ее в качестве базы ввода. Шестнадцатеричные цифры должны быть в верхнем регистре, чтобы избежать конфликтов с командами постоянного тока, и ограничены A-F. Команда o
делает то же самое для выходной базы, но имейте в виду, что входная база впоследствии повлияет на синтаксический анализ каждого числового значения, поэтому обычно рекомендуется сначала установить выходную базу. Поэтому 10o
устанавливает систему счисления вывода на текущую систему счисления ввода, но обычно не на 10 (десять). Тем не менее Ao
сбрасывает базу вывода на 10 (десять), независимо от базы ввода. Чтобы прочитать значения, команды K
, I
и O
поместят текущую точность, входную систему счисления и выходную систему счисления в верхнюю часть стека.
В качестве примера для преобразования из шестнадцатеричного в двоичное:
$ echo 16i2o DEADBEEFp | dc 11011110101011011011111011101111
В дополнение к этим базовым арифметическим и стековым операциям, dc включает поддержку макросов, условных выражений и сохранения результатов для последующего поиска.
Механизм, лежащий в основе макросов и условных операторов, - это регистр, который в dc является местом хранения с однозначным именем, которое может быть сохранено и получено из: sc
выталкивает вершину стека и сохраняет его в регистре c, а lc
помещает значение регистра c в стек. Например:
3 sc 4 lc * p
Регистры также могут рассматриваться как вторичные стеки, поэтому значения можно перемещать и выталкивать между ними и основным стеком с помощью S
и L
команды.
Строковые значения заключаются в символы [
и ]
и могут быть помещены в стек и сохранены в регистрах. Команда a
преобразует младший байт числового значения в символ ASCII, или, если верхняя часть стека является строкой, она заменит ее первым символом строка. Невозможно создать строки или выполнить манипуляции со строкой, кроме выполнения их с помощью команды x
или печати с помощью команды P
.
Символ #
начинает комментарий до конца строки.
Макросы затем реализуются, позволяя регистрам и записям стека быть строками, а также числами. Строку можно напечатать, но ее также можно выполнить (то есть обработать как последовательность команд постоянного тока). Так, например, мы можем сохранить макрос, чтобы добавить единицу, а затем умножить на 2 в регистре m:
[1 + 2 *] sm
, а затем (используя x
, которая выполняет верхнюю часть стека), мы можем использовать ее так:
3 lm xp
Наконец, мы можем использовать этот макро-механизм для предоставления условных выражений. Команда = r
извлечет два значения из стека и выполнит макрос, хранящийся в регистре r
, только если они равны. Таким образом, это напечатает строку равную
, только если верх стека равен 5:
[[equal] p] sm 5 = m
Другие условные выражения >
, !>
, <
, !<
, !=
, который выполнит указанный макрос, если два верхних значения в стеке больше, меньше или равны ("не больше"), меньше, больше или равны ("не меньше") и не равно соответственно.
Циклы тогда возможны путем определения макроса, который (условно) повторно запускается. Простой факториал вершины стека может быть реализован как:
# F (x): return x! # если x-1>1 # return x * F (x-1) # в противном случае # return x [d1-d1Команда
1Q
завершит работу макроса, позволяя досрочно вернуться.q
завершит работу с двух уровней макросов (и с самого dc, если в стеке вызовов меньше двух уровней).z
подтолкнет текущую глубину стека перед операциейz
.ПримерыСуммирование всего стека
Это реализовано с помощью макроса, хранящегося в регистре
a
, который условно вызывает сам себя, каждый раз выполняя сложение, пока в стеке не останется только одно значение. Операторz
используется для добавления количества записей в стеке в стек. Оператор сравнения>
извлекает два значения из стека при сравнении.dc -e "1 2 4 8 16 100 0d [+ 2z>a] salaxp"И результат равен 131.
Суммирование всех выражений постоянного тока в виде строк из файла
Чистое число является допустимым выражением постоянного тока, поэтому его можно использовать для суммирования файла, в котором каждая строка содержит одно число.
Это снова реализуется с помощью макроса, хранящегося в регистре
a
, который условно вызывает себя, выполняя каждый раз сложение, пока в стеке не останется только одно значение.файл cat | dc -e "0d [? + 2z>a] salaxp"Оператор
?
считывает другую команду из входного потока. Если строка ввода содержит десятичное число, это значение добавляется в стек. Когда входной файл достигает конца файла, команда принимает значение NULL, и значение в стек не добавляется.{эхо "5"; эхо «7»; } | dc -e "0d [? + 2z>a] salaxp"И результат 12.
Строки ввода также могут быть сложными командами постоянного тока.
{echo "3 5 *"; эхо «4 3 *»; echo "5dd ++"} | dc -e "0d [? + 2z>a] salaxp"И результат 42.
Обратите внимание, что, поскольку dc поддерживает произвольную точность, нет никаких проблем с числовым переполнением или потерей точности, нет независимо от того, сколько строк содержит входной поток, в отличие от аналогичного краткого решения в AWK.
Обратными сторонами этого решения являются: цикл прекращается при обнаружении пустой строки во входном потоке (технически, любая строка ввода, которая не добавить в стек хотя бы одно числовое значение); и, для обработки отрицательных чисел, начальные экземпляры '-' для обозначения отрицательного знака должны быть изменены на '_' во входном потоке из-за нестандартного отрицательного знака dc. Оператор
?
в dc не обеспечивает четкого способа отличить чтение пустой строки от чтения конца файла.Преобразование единиц измерения
В качестве примера относительно простой программы на dc, эта команда (в 1 строке):
dc -e '[[Введите число (в метрах) или 0 для выхода] psj] sh [q] sz [lhx? D0 = z10k39.370079 *.5 + 0k12 ~ 1 / rn [футов] Pn [дюймов] P10Pdx] dx 'преобразует расстояния из метров в футы и дюймы; большая часть этого связана с запросом ввода, печатью вывода в подходящем формате и циклическим циклом для преобразования другого числа.
Наибольший общий делитель
В качестве примера приведена реализация алгоритма Евклида для нахождения GCD :
dc -e '?? [ dSarLa% d0Факториал
Вычисление факториала входного значения,
dc -e '? [Q] sQ [d1 = Qd1-lFx *] dsFxp'Quines in dc
Там же выходят также quines на языке программирования dc, программы, которые производят свой исходный код в качестве вывода.
dc -e '[91Pn [dx] 93Pn] dx' dc -e '[91PP93P [dx] P] dx'Печать всех простых чисел
echo '2p3p [dl! D2 + s!% 0 = @ l! L ^! <#]s#[s/0ds^][email#160;protected] [p]s[ddvs^3s!l#x0<2+l.x]ds.x' | dcЭта программа была написана Мишелем Шарпантье. Он выводит последовательность простых чисел. Обратите внимание, что его можно сократить на один символ, что кажется минимальным решением.
echo '2p3p [dl! D2 + s!% 0 = @ l! L ^! <#]s#[0*ds^][email#160;protected] [p]s[ddvs^3s!l#x0<2+l.x]ds.x' | dcЦелочисленное разложение
dc -e' [n =] P? [P] s2 [lip / dli% 0 = 1dvsr] s12sid2% 0 = 13sidvsr [dli% 0 = 1lrli2 + dsi!>.] Ds.xd1 <2'Эта программа также была написана Мишелем Шарпантье.
Существует более короткий
dc - e "[n =] P? [lfp / dlf% 0 = Fdvsr] sF [dsf] sJdvsr2sf [dlf% 0 = Flfdd2% + 1 + sflrи более быстрое решение (попробуйте с 200-битным числом 2- 1 (ввод
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Обратите внимание, что последнее можно даже ускорить, если доступ к константе заменен доступом к регистру.
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Обмен ключами Диффи-Хеллмана
Более сложный пример использования постоянного тока, встроенный в сценарий Perl, выполняет обмен ключами Диффи-Хеллмана. Это было популярно как блок подписи среди шифровальщиков во время дебатов по ITAR, где Короткий скрипт может быть запущен только с Perl и dc, вездесущими программами в Unix-подобных операционных системах:
#! / usr / bin / perl - -export-a-crypto-system-sig Diffie-Hellman-2 -строчки ($ g, $ e, $ m) = @ARGV, $ m || die "$ 0 gen exp mod \ n"; print ʻecho "16dio1 [d2% Sa2 / d0Прокомментированная версия немного проще для понимания и показывает, как использовать циклы, условные выражения и команду
q
для возврата из макроса. С GNU версии dc можно использовать команду|
для модульного возведения в степень произвольной точности без необходимости писать функцию X.#! / usr / bin / perl my ($ g, $ e, $ m) = map {"\ U $ _"} @ARGV; die "$ 0 gen exp mod \ n", если не $ m; print ʻecho $ g $ e $ m | dc -e '# Шестнадцатеричный ввод и вывод 16dio # Чтение m, e и g из стандартного ввода в одной строке? SmSeSg # Функция z: вернуть g * вершину стека [lg *] sz # Функция Q: удалить вершину стека и вернуть 1 [sb1q] sQ # Функция X (e) : рекурсивно вычислить g ^ e% m # Это то же самое, что Sm ^ Lm%, но обрабатывает произвольно большие показатели. # Стек на входе: e # Стек на выходе: g ^ e% m # Поскольку e может быть очень большим, это использует свойство 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, если e == 0 (в противном случае st ack: e) d 2% Sa # Сохранить e% 2 в a (stack: e) 2 / # compute e / 2 lXx # call X (e / 2) d * # compute X (e / 2) ^ 2 La1 = z # умножить на g, если e% 2 == 1 lm% # compute (g ^ e)% m] SX le # Загрузить e из регистра lXx # compute g ^ e% mp # Распечатать результат '';См. ТакжеСсылкиВнешние ссылки
- Пакет dc в репозиториях Debian GNU / Linux
dc (1)
– Plan 9 Руководство программиста, том 1- Собственный порт Windows из bc, который включает dc.
- dc, встроенный в веб-страницу