Вы можете сравнить только два числа dc
как:
dc -e "[$1]sM $2d $1<Mp"
... где "$1"
ваше максимальное значение и "$2"
число, которое вы напечатаете, если оно меньше, чем "$1"
. Это также требует GNU dc
- но вы можете сделать то же самое, например:
dc <<MAX
[$1]sM $2d $1<Mp
MAX
В обоих вышеупомянутых случаях вы можете установить точность, отличную от 0 (по умолчанию), например ${desired_precision}k
. Для обоих также необходимо убедиться, что оба значения являются определенно числами, потому что они dc
могут выполнять system()
вызовы с !
оператором.
С помощью следующего небольшого сценария (и следующего) вы также должны проверить ввод - например, grep -v \!|dc
или что-то, чтобы надежно обрабатывать произвольный ввод. Вы также должны знать, что dc
интерпретирует отрицательные числа с _
префиксом, а не с -
префиксом - потому что последний является оператором вычитания.
Кроме того, этот сценарий dc
будет читать столько последовательных \n
чисел, разделенных электронной строкой, сколько вы захотите предоставить, и печатать для каждого либо ваше $max
значение, либо входные данные, в зависимости от того, что меньше из wo:
dc -e "${max}sm
[ z 0=? d lm<M p s0 lTx ]ST
[ ? z 0!=T q ]S?
[ s0 lm ]SM lTx"
Так ... каждый из этих [
квадратных скобки ]
просторов является dc
строка объект, S
aved каждого к соответствующему массиву - любой один из T
, ?
или M
. Помимо некоторых других вещейdc
могут быть связаны со строкой , она также может быть использована x
как макрос. Если вы все устроите правильно, полноценный маленький dc
скрипт будет собран достаточно просто.
dc
работает в стеке . Все входные объекты накладываются друг на друга последним - каждый новый входной объект помещает последний верхний объект и все объекты под ним в стек по одному при его добавлении. Большинство ссылок на объект являются к верхнему значению стека, и большинство ссылок поп , что вершины стека (который тянет все объекты под ним до одного) .
Помимо основного стека, есть также (как минимум) 256 массивов, и каждый элемент массива имеет свой собственный стек. Я не использую большую часть этого здесь. Я просто храню строки, как уже упоминалось, так что я могу l
oad их при желании и еx
вывести их условно, и я s
порвал $max
значение в верхней части m
массива.
В любом случае, эта небольшая часть dc
делает, в основном, то, что делает ваш shell-скрипт. Он использует GNU-изм-e
опцию - как dc
правило, берет свои параметры из стандарта - но вы можете сделать то же самое, например:
echo "$script" | cat - /dev/tty | dc
... если бы $script
выглядело, как указано выше.
Это работает как:
lTx
- Это l
выдает и x
извлекает макрос, хранящийся в верхней части T
(для теста, я думаю, я обычно выбираю эти имена произвольно) .
z 0=?
- T
Эст затем проверяет глубину стека ш / z
и, если стек пуст (читай: содержит 0 объектов) он вызывает ?
макрос.
? z0!=T q
- ?
Макрос назван по имени ?
dc
встроенной команды, которая читает строку ввода из stdin, но я также добавил еще один z
тест глубины стека, чтобы он мог q
использовать всю маленькую программу, если он вытянет пустую строку или нажмет EOF. Но если это !
не так и вместо этого успешно заполняет стек, он вызывает T
est снова.
d lm<M
- T
Затем est d
обновит верхнюю часть стека и сравнит его с $max
(как хранится в m
) . Если m
это меньшее значение, dc
вызывает M
макрос.
s0 lm
- M
просто выбрасывает верх стека и сбрасывает его в фиктивный скаляр 0
- просто дешевый способ вытолкнуть стек. Это также l
oads m
снова, прежде чем вернуться кT
EST.
p
- Это означает, что если m
он меньше текущей вершины стека, то m
заменяет его (в d
любом случае его дубликат) и находится здесьp
печатается, в противном случае это не так, и независимо от того, какой ввод был p
введен, вместо этого печатается.
s0
- После этого (потому p
что стек не выталкивается) мы сбрасываем верхнюю часть стека в0
снова , а затем ...
lTx
- рекурсивно l
получается T
еще разx
ecute его снова.
Таким образом, вы можете запустить этот небольшой отрывок и в интерактивном режиме набрать цифры на своем терминале и dc
напечатать на вас либо введенный вами номер, либо значение, $max
если набранный номер был больше. Он также будет принимать любой файл (например, канал) в качестве стандартного ввода. Он будет продолжать цикл чтения / сравнения / печати, пока не встретит пустую строку или EOF.
Некоторые замечания по этому поводу, хотя - я написал это только для того, чтобы эмулировать поведение в вашей функции оболочки, так что она надежно обрабатывает только одно число в строке. dc
тем не менее, вы можете обработать столько чисел, разделенных пробелом на строку, сколько вы захотите. Однако из-за своего стека последнее число в строке оказывается первым, с которым он работает, и поэтому, как написано,dc
выводит его вывод в обратном порядке, если вы напечатаете / напечатаете более одного числа в строке. обработать это значит сохранить строку в массиве, а затем обработать ее.
Так:
dc -e "${max}sm
[ d lm<M la 1+ d sa :a z0!=A ]SA
[ la d ;ap s0 1- d sa 0!=P ]SP
[ ? z 0=q lAx lPx l?x ]S?
[q]Sq [ s0 lm ]SM 0sa l?x"
Но ... я не знаю, хочу ли я объяснить это с такой же глубиной. Достаточно сказать, что при dc
чтении каждого значения в стеке он сохраняет либо свое значение, либо $max
значение в индексированном массиве, и, как только он обнаруживает, что стек снова пуст, он затем печатает каждый индексированный объект, прежде чем пытаться прочитать другой. строка ввода.
И так, пока первый скрипт делает ...
10 15 20 25 30 ##my input line
20
20
20
15
10 ##see what I mean?
Второй делает:
10 15 20 25 30 ##my input line
10 ##that's better
15
20
20 ##$max is 20 for both examples
20
Вы можете обрабатывать числа с произвольной точностью, если вы сначала задали их с помощью k
команды. Кроме того, вы можете изменять i
радиусы nput или o
utput независимо друг от друга, что иногда может быть полезно по причинам, которые вы не ожидаете. Например:
echo 100000o 10p|dc
00010
... который сначала устанавливает dc
выходной радиус 100000, а затем печатает 10.