постоянный ток (компьютерная программа)
Оригинальный автор(ы) | Лоринда Черри , Роберт Моррис ( AT&T Bell Laboratories ) |
---|---|
Разработчик(и) | Различные с открытым исходным кодом и коммерческие разработчики разработчики |
Написано в | Б |
Операционная система | Unix , Unix-подобные , Plan 9 |
Платформа | Кросс-платформенный |
Тип | Команда |
dc ( настольный калькулятор ) — кроссплатформенный калькулятор обратного польского языка , поддерживающий арифметику произвольной точности . [1] Его написали Лоринда Черри и Роберт Моррис из Bell Labs . [2] Это одна из старейших утилит Unix , предшествовавшая даже изобретению языка программирования C. Как и другие утилиты того времени, она обладает мощным набором функций, но кратким синтаксисом. [3] [4] Традиционно программа-калькулятор bc (с инфиксной записью ) реализовывалась поверх dc.
В этой статье приводятся несколько примеров в попытке дать общее представление о языке; Полный список команд и синтаксиса можно найти на странице руководства по конкретной реализации.
История
[ редактировать ]dc — старейшая из сохранившихся языковых программ Unix . Когда ее родная лаборатория Bell Labs получила PDP-11 , dc, написанный на B , стал первым языком, который можно было запустить на новом компьютере, еще до появления ассемблера. [2] Кен Томпсон высказал мнение, что dc была самой первой программой, написанной на машине. [5]
Основные операции
[ редактировать ]Чтобы умножить четыре и пять в постоянном токе (обратите внимание, что большая часть пробелов необязательна):
$ 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
вычисляет квадратный корень из вершины стека и _
используется для ввода отрицательного числа):
12 _3 4 ^ + 11 / v 22 - p
Чтобы поменять местами два верхних элемента стека, используйте команду r
команда. Чтобы дублировать верхний элемент, используйте d
команда.
Ввод/вывод
[ редактировать ]Чтобы прочитать строку со стандартного ввода , используйте команду ?
команда. При этом строка оценивается так, как если бы это была команда постоянного тока, поэтому необходимо, чтобы она была синтаксически правильной и представляла потенциальную проблему безопасности, поскольку !
Команда dc позволяет выполнять произвольную команду.
Как упоминалось выше, p
печатает верхнюю часть стека с новой строкой после нее. n
извлекает верхнюю часть стека и печатает ее без завершающего символа новой строки. f
печатает весь стек, по одной записи в строке.
dc также поддерживает произвольные входные и выходные системы счисления . i
Команда извлекает вершину стека и использует ее в качестве базы ввода. Шестнадцатеричные цифры должны быть в верхнем регистре, чтобы избежать конфликтов с командами постоянного тока, и ограничиваются AF. 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
команда.
The #
символ начинает комментарий до конца строки.
Макросы
[ редактировать ]Затем реализуются макросы, позволяющие регистрам и записям стека быть не только числами, но и строками. Строку можно вывести на печать, но ее также можно выполнить (т.е. обработать как последовательность команд постоянного тока). Например, мы можем сохранить макрос, чтобы добавить единицу, а затем умножить ее на 2 в регистр m:
[1 + 2 *] sm
а затем (с помощью x
команда, которая выполняет вершину стека), мы можем использовать ее следующим образом:
3 lm x p
Условные предложения
[ редактировать ]Наконец, мы можем использовать этот макромеханизм для предоставления условий. Команда =r
извлекает два значения из стека и выполняет макрос, хранящийся в регистре r
только если они равны. Итак, это печатает строку equal
только если два верхних значения в стеке имеют одинаковое значение:
[[equal]p] sm 5 5 =m
Другие условные обозначения >
, !>
, <
, !<
, !=
, которые выполняют указанный макрос, если два верхних значения в стеке больше, меньше или равны («не больше»), меньше, больше или равны («не меньше») и не равны соответственно . Обратите внимание, что порядок операндов при сравнении неравенств противоположен порядку в арифметике; 5 3 - оценивается как 5 - 3 = 2
, но 5 3 <t запускает содержимое t зарегистрироваться, потому что 3 < 5
.
Петли
[ редактировать ]В этом случае цикл становится возможным путем определения макроса, который (условно) повторно вызывает себя. Простой факториал вершины стека может быть реализован как:
# F(x): return x! # if x-1 > 1 # return x * F(x-1) # otherwise # return x [d1-d1<F*]dsFxp
The 1Q
команда выходит из макроса, позволяя вернуться раньше. q
завершает работу с двух уровней макросов (и самого dc, если в стеке вызовов меньше двух уровней). z
помещает текущую глубину стека перед z
операция.
Примеры
[ редактировать ]Суммируем весь стек
[ редактировать ]Это реализуется с помощью макроса, хранящегося в реестре. a
который условно вызывает сам себя, каждый раз выполняя сложение, пока в стеке не останется только одно значение. z
Оператор используется для помещения количества записей из стека в стек. Оператор сравнения >
извлекает два значения из стека при сравнении.
dc -e "1 2 4 8 16 100 0d[+z1<a]dsaxp"
И результат 131.
Суммирование всех выражений постоянного тока в виде строк из файла
[ редактировать ]Простое число является допустимым выражением постоянного тока, поэтому его можно использовать для суммирования файла, в котором каждая строка содержит одно число.
Это снова реализовано с помощью макроса, хранящегося в регистре. a
который условно вызывает сам себя, каждый раз выполняя сложение, пока в стеке не останется только одно значение.
cat file | dc -e "0d[?+z1<a]dsaxp"
The ?
оператор считывает другую команду из входного потока. Если строка ввода содержит десятичное число, это значение добавляется в стек. Когда входной файл достигает конца файла, команда становится нулевой, и в стек не добавляется никакое значение.
{ echo "5"; echo "7"; } | dc -e "0d[?+z1<a]dsaxp"
И результат 12.
Входные строки также могут представлять собой сложные команды постоянного тока.
{ echo "3 5 *"; echo "4 3 *"; echo "5dd++"; } | dc -e "0d[?+z1<a]dsaxp"
И результат 42.
Обратите внимание: поскольку dc поддерживает произвольную точность, не возникает проблем с числовым переполнением или потерей точности, независимо от того, сколько строк содержит входной поток, в отличие от аналогичного лаконичного решения в AWK .
Недостатками этого решения являются: цикл останавливается при обнаружении пустой строки во входном потоке (технически это любая строка ввода, которая не добавляет хотя бы одно числовое значение в стек); а для обработки отрицательных чисел ведущие экземпляры '-' для обозначения отрицательного знака должны быть заменены на '_' во входном потоке из-за нестандартного отрицательного знака dc. ?
Оператор в dc не обеспечивает четкого способа отличить чтение пустой строки от чтения конца файла.
Преобразование единиц измерения
[ редактировать ]В качестве примера относительно простой программы на dc, эта команда (в 1 строку):
dc -e '[[Enter a number (metres), or 0 to exit]PAP]sh[q]sz[lhx?d0=zAk.0254/.5+0kC~1/rn[ feet ]Pn[ inches]PAPdx]dx'
конвертирует расстояния из метров в футы и дюймы; большая часть его связана с запросом ввода, выводом вывода в подходящем формате и циклическим преобразованием другого числа.
Наибольший общий делитель
[ редактировать ]В качестве примера, вот реализация алгоритма Евклида для поиска НОД :
dc -e '??[dSarLa%d0<a]dsax+p' # shortest
dc -e '[a=]P?[b=]P?[dSarLa%d0<a]dsax+[GCD:]Pp' # easier-to-read version
Факториал
[ редактировать ]Вычисление факториала входного значения,
dc -e '?[q]sQ[d1=Qd1-lFx*]dsFxp'
Какие в Вашингтоне
[ редактировать ]существуют также куайны В языке программирования dc ; программы, которые создают исходный код в качестве вывода.
dc -e '[91Pn[dx]93Pn]dx'
dc -e '[91PP93P[dx]P]dx'
Печать всех простых чисел
[ редактировать ]echo '2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l#x0<&2+l.x]ds.x' | dc
Эту программу написал Мишель Шарпантье. Он выводит последовательность простых чисел. Обратите внимание, что возможна более короткая реализация, которая читает на четырнадцать символов меньше.
echo '2p3p[pq]s$[l!2+ds!l^<$dl!%0<#]s#[+dvs^1s!l#x2l.x]ds.x' | dc
Целочисленная факторизация
[ редактировать ]dc -e '[n=]P?[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2'
Эту программу также написал Мишель Шарпантье. [6]
Есть более короткий
dc -e "[n=]P?[lfp/dlf%0=Fdvsr]sF[dsf]sJdvsr2sf[dlf%0=Flfdd2%+1+sflr<Jd1<M]dsMx"
и более быстрое решение (попробуйте с 200-битным номером 2 200 -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<M"
Обратите внимание, что последнее можно ускорить еще больше, если доступ к константе заменить доступом к регистру.
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"
Вычисление Пи
[ редактировать ]Реализация алгоритма Чудновского на языке программирования dc. По мере работы программа будет печатать все лучшие и лучшие приближения. Но поскольку число «пи» является трансцендентным, программа будет продолжать работу до тех пор, пока не будет прервана или пока не исчерпаются ресурсы машины, на которой она выполняется.
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'
Быстрая реализация той же формулы «разделяй и властвуй», размер которой удваивается на каждой итерации. Он вычисляет конечное число, если суммирует его как точное рациональное число и выполняет только одно большое деление и извлечение квадратного корня за итерацию. Он быстрый, но все равно быстро замедляется по мере увеличения размера фракции.
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'
Обмен ключами Диффи-Хеллмана
[ редактировать ]Более сложный пример использования постоянного тока, встроенный в сценарий Perl , выполняет обмен ключами Диффи-Хеллмана . Это было популярно в качестве сигнатурного блока среди шифропанков во время дебатов в ITAR , где короткий сценарий можно было запустить только с помощью Perl и dc, вездесущих программ в Unix-подобных операционных системах: [7]
#!/usr/bin/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`
Версия с комментариями немного проще для понимания и показывает, как использовать циклы, условные выражения и q
команда возврата из макроса. В версии dc для GNU |
Эту команду можно использовать для возведения в степень модульного возведения в степень произвольной точности без необходимости писать функцию X.
#!/usr/bin/perl
my ($g, $e, $m) = map { "\U$_" } @ARGV;
die "$0 gen exp mod\n" unless $m;
print `echo $g $e $m | dc -e '
# Hex input and output
16dio
# Read m, e and g from stdin on one line
?SmSeSg
# Function z: return g * top of stack
[lg*]sz
# Function Q: remove the top of the stack and return 1
[sb1q]sQ
# Function X(e): recursively compute g^e % m
# It is the same as Sm^Lm%, but handles arbitrarily large exponents.
# Stack at entry: e
# Stack at exit: g^e % m
# Since e may be very large, this uses the property that 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 (otherwise, stack: e)
d 2% Sa # Store e%2 in a (stack: e)
2/ # compute e/2
lXx # call X(e/2)
d* # compute X(e/2)^2
La1=z # multiply by g if e%2==1
lm % # compute (g^e) % m
] SX
le # Load e from the register
lXx # compute g^e % m
p # Print the result
'`;
Переменные среды
[ редактировать ]Если переменная среды DC_LINE_LENGTH существует и содержит целое число больше 1 и меньше , вывод цифр числа (в соответствии с базой вывода) будет ограничен этим значением, после чего будут вставлены обратные косые черты и символы новой строки. Длина строки по умолчанию равна 70. Специальное значение 0 отключает разрывы строк.
См. также
[ редактировать ]- bc (язык программирования)
- Методы ввода калькулятора
- Калькуляторы HP
- Штабелируемая машина
- Обратная польская запись
Ссылки
[ редактировать ]- ^ Linux пользователя по командам Руководство : калькулятор произвольной точности –
- ^ Jump up to: а б Макилрой, доктор медицины (1987). Читатель Research Unix: аннотированные выдержки из Руководства программиста, 1971–1986 (PDF) (Технический отчет). CSTR. Лаборатории Белла. 139.
- ^ «Источники страницы руководства для 7-го издания Unix dc» . Архивировано из оригинала 23 августа 2004 г. Проверено 23 июня 2004 г.
- ^ Ричи, Деннис М. (сентябрь 1979 г.). «Эволюция системы разделения времени Unix» . Архивировано из оригинала 6 мая 2010 г.
- ^ Брайан Керниган и Кен Томпсон. Невероятное удовольствие для любого участника Vintage Computer Fest 2019: Керниган берет интервью у Томпсона о Unix . Ютуб. Событие происходит в 29:45 . Проверено 3 сентября 2019 г.
- ^ «Расширенное руководство по написанию сценариев Bash, глава 16, пример 16-52 (факторизация)» . Проверено 20 сентября 2020 г.
- ^ Адам Бэк. «Диффи-Хеллман в 2 строках Perl» . Проверено 5 января 2009 г.
Внешние ссылки
[ редактировать ]- Пакет dc в Debian GNU/Linux репозиториях
- Linux по основным командам Руководство –
- Plan 9 , том 1 Руководство программиста –
- Собственный порт bc для Windows , который включает dc.