Совокупность чисел и разделение по длине. Простой рекурсивный sum
, как правило, является одним из первых примеров, которые вы увидите в любом учебнике SML. Вам нужно будет иметь пустой список из списка sum
для оценки 0.0
, а не 0
, чтобы убедиться, что тип возврата real
. Как только вы определите функцию sum
, вы можете определить average
в 1 строке, используя sum
и встроенную функцию length
. Подложка заключается в том, что SML не позволяет реальному делиться на int. Вы можете использовать функцию преобразования Real.fromInt
по длине перед делением суммы на нее. Существует некоторая неэффективность при переходе по одному и тому же списку дважды, один раз, чтобы суммировать его и один раз рассчитать его длину, но нет оснований беспокоиться о таких вещах, когда вы впервые изучаете язык.
На Edit: Так как вы нашли естественное решение и разделяете его в комментариях, здесь более идиоматическая версия, которая вычисляет среднее за один проход по списку:
fun average nums =
let
fun av (s,n,[]) = s/Real.fromInt(n)
| av (s,n,x::xs) = av (s+x,n+1,xs)
in
av (0.0, 0, nums)
end;
Он работает путем определения вспомогательная функция которая делает тяжелый подъем. Они широко используются в функциональном программировании. В отсутствие изменчивого состояния общий трюк заключается в том, чтобы явно передавать в качестве параметров величины, которые были бы последовательно модифицированы соответствующей петлей на императивном языке. Такие параметры часто называются accumulators
, так как они обычно накапливают растущие списки, текущие суммы, текущие продукты и т. Д. Здесь s
и n
- это аккумуляторы с суммой элементов и n
- длина списка. В базовом случае (s,n,[])
больше нечего накапливать, поэтому возвращается окончательный ответ. В случае без основания (s,n,x::xs)
, s
и n
соответствующим образом изменяются и передаются вспомогательной функции вместе с хвостом списка. Определение av
равно tail-recursive, следовательно, будет работать со скоростью цикла без увеличения стека. Единственное, что нужно сделать общей функции average
, - это вызвать вспомогательную функцию с соответствующими начальными значениями. let ... helper def ... in ... helper called with start-up values ...end
- распространенная идиома, используемая для предотвращения загромождения верхнего уровня программы вспомогательными функциями.
A + за предоставление руководства/объяснения ученику вместо того, чтобы просто дать ответ, который ничего не учит. – naomik
@john Спасибо за помощь! В учебном пособии мы обсуждали это с помощью нескольких функций. Мы не должны были использовать какую-либо сборку в библиотеках (т. Е. Real.fromInt). Поэтому я попробовал это с помощью метода нескольких функций и получил что-то вроде этого (что работает): ** sum function: ** 'fun sum (h :: t) = h + sum (t) | sum (nil) = 0.0; ' ** длина функция: ** ' fun length (h :: t) = 1.0 + length (t) | длина (ноль) = 0,0; ' ** Полная функция: ** ' fun average (l) = sum (l)/length (l); ' – opheliaxo
@opheliaxo Nice. Я считаю более естественным поставить основной случай перед рекурсивным случаем, но это в основном вопрос вкуса и, возможно, конвенции. Поскольку мне больше не нужно беспокоиться о выполнении чьей-либо домашней работы, прежде чем им будет приятно разобраться в этом, я отредактировал свой ответ, чтобы включить то, что я считаю самым идиоматичным и эффективным способом вычисления среднего значения в SML. –