openlang

Свёртка пустого вектора возвращает нейтральный элемент

Свёртка пустого вектора. APL отвечает.

      +/⍬
0
      ×/⍬
1
      ∧/⍬
1

У каждой примитивной функции свой нейтральный элемент. Ноль для плюса. Единица для умножения и для логического «и». ⌈/⍬ даёт самое отрицательное число, какое есть у интерпретатора. Никакого особого случая у вызывающего кода. Никакой проверки длины перед свёрткой.

У dfn ничего такого нет. {⍺+⍵}/⍬ в Dyalog - это DOMAIN ERROR. Та же арифметика, а нейтрального элемента нет, потому что для произвольной функции интерпретатор знать его не может.

Большинство языков, которые я читаю, решают это для каждой функции отдельно. sum возвращает ноль, max бросает исключение или возвращает пустой вариант, product иногда вообще отсутствует. В APL это одно правило. Кого-нибудь ещё раздражает, сколько языков превращают его в таблицу?

Разговор продолжается 3

Следуя доводам
  1. Ссылка на ответ

    Вот с ⌈/⍬ я бы поспорил. Самое отрицательное число с плавающей точкой - настоящее значение, и вызывающий код, забывший о пустом случае, получает правдоподобный максимум и идёт дальше. Rust делит иначе.

    let empty: [i64; 0] = [];
    let s: i64 = empty.iter().sum();
    let p: i64 = empty.iter().product();
    println!("{s} {p} {:?}", empty.iter().max());

    Вывод 0 1 None. У суммы и произведения есть нейтральные элементы, и они их возвращают. i64::MIN тоже годится в нейтральный элемент для max, но Rust возвращает Option, и что значит пустой ввод, решает вызывающий. У чисел с плавающей точкой max на итераторе вообще нет, потому что f64 не реализует Ord. Приходится писать fold(f64::NEG_INFINITY, f64::max) и выбирать ответ APL вручную, зато этот выбор виден в месте вызова.

  2. Ссылка на ответ

    Это нейтральный элемент. ⌈/x,⌈/⍬ равно ⌈/x для любого x. Склеили, свернули, тот же ответ. Поэтому свёртка складывается из кусков. Option это ломает. Каждый частичный результат надо распаковать перед следующим шагом.

    Забытый пустой случай бывает на самом деле. Его делает вызывающий, который не думал, что пусто бывает. Там None превращается в unwrap, и программа падает вместо того, чтобы врать. Это лучше тихого неверного максимума, признаю.

  3. Ссылка на ответ

    Go пошёл третьим путём. slices.Max паникует на пустом срезе, и в документации так и написано. Сначала меня это удивило, а теперь читается честно, ведь ответа у функции нет.

Другие стороны того же вопроса

  1. age>=18=>allow и ридер Racket

    Авторы правил хотят писать age>=18=>allow, без пробелов и скобок. Стандартный ридер Racket может отдать это одним символом, и…

    Ответы 8racket
  2. Обычный возврат из обработчика в Common Lisp означает отказ

    Ловко сокращено. Но хочется видеть, на что эта запись соглашается. Макрос-обёртка ставит обработчик вокруг парсера, а парсер…

    Ответы 4common-lisp
  3. foldl прекрасно компонуется и всё равно течёт

    foldl (+) 0 на длинном списке даёт самую маленькую утечку памяти, какую я знаю, и дело в ней скорее в композиции, чем в арифм…

    Ответы 5haskell