Этот сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

Кроссплатформенность и bc

Как мы все знаем, в мире существует две версии bc — более скромная по возможностям версия GNU, которая используется в Линуксе, и более развитая, которая, например, используется в командной строке МакОСи.

Они довольно серьёзно различаются — когда я писал игру «Виселица» на этом языке, мне пришлось сделать две версии для разных синтаксисов.

В принципе, можно писать кроссплатформенно, если использовать подмножество, которое поддерживают обе версии, но есть одна неприятность — функция получения случайного числа в гну-версии называется random(), а в более продвинутой — rand() (точнее там их целое семейство, но функции random() там нет).

Поскольку в языке нет способа узнать определена функция или нет, нужно каким-то образом отличать одну версию от другой и вызывать, в зависимости от результата, ту или другую функцию.

Каким же образом мы можем это сделать?

Вначале я возлагал надежду на переменную seed. В GNU её нету, то есть она содержит ноль, а в продвинутой версии она содержит инициализирующее значение. Как будто этого вполне достаточно, но, во-первых, никто не обещает, что она по какой-то случайности не получит нулевое значение, а во-вторых, её вполне могут реализовать в версии GNU. Нужно что-то принципиально другое, что в будущем не будет работать иначе, правка или реализация которого сломала бы обратную совместимость.

Такую особенность я нашёл — она заключается в приоритете обработки операции отрицания, которая записывается как восклицательный знак.

Из-за этой разницы две версии по-разному вычисляют конструкцию !1+1. В продвинутой версии порядок выполнения следующий: (!1)+1, что даёт нам единицу, а в гнушной приоритет другой и конструкция выполнится так: !(1+1), что даёт нам ноль.

Основываясь на этом, мы можем написать универсальную функцию для получения случайного числа:

define rnd() {
    /* Checking the bc version */
    if (!1+1) {
        return rand()
    } else {
        return random() * random()
    }
}

P. S. Надо помнить, что в гну-версии функция получения случайного числа сломана, но есть надежда, что скоро её починят и ей можно будет пользоваться.

P. P. S. Я умножаю random() на random(), чтобы хоть немного выровнять по диапазону значений rand() и random(), правда распределение, конечно, изменится.

1 комментарий
Raidenyn 27 дн

Перемножение равномерно распределеных случайных чисел сдвигает выборку к нормальному распределению. Не знаю на сколько это важно в Вашей программе, но в общем случае это может быть проблемой.

Евгений Степанищев 27 дн

Так и есть, в телеграме обсудили уже. Я так и не определился пока — проблема это или нет. Наверное стоит убрать перемножение, раз это столько вопросов вызывает.