Как читать этот функциональный код
Возникли проблемы при чтении (интерпретации) этого функционального кода Miranda.
g = (foldr (+) 0) . (foldr ((:) . ((#) . (:[]))) [])
Я знаю что это делает
- Рассчитайте размер списка, взяв длину через
#
- Создание списка из одного элемента, содержащего вышеуказанную длину исходного списка ввода
- Свернуть новый список, используя
foldr
в одно целое число с операцией+0
на каждом элементе
Однако я смущен скобками и не вижу, куда вводится список ввода. Что делает самый правый []
конструктор делать?
Кроме того, почему этот код работает только через функцию g, но если я вызываю его напрямую, он выдает ошибку?
2 ответа
Короче, g
это функция, которая возвращает длину списка
Давайте разберем функцию на несколько частей.
|| returns 1 for any input.
|| return_one "hoobar" = 1
return_one :: * -> num
return_one = (#) . (:[])
|| ignore first argument, insert 1 to the second argument.
|| insert_one "hoobar" [4,5,6] = [1,4,5,6]
insert_one :: *->[num]->[num]
insert_one = (:) . return_one
|| sum of list.
|| sum_list [7,8,9] = 24
sum_list :: [num] -> num
sum_list = foldr (+) 0
|| generate list of 1 which as the same length of original list.
|| change_one ["apple","banana"] = [1,1]
change_one :: [*] -> [num]
change_one = foldr insert_one []
|| return the length of the list.
|| g ["apple","banana"] = 2
g :: [*] -> num
g = sum_list . change_one
Я бы объяснил некоторые запутанные функции.
return_one
(:[])
это функция, которая создает список из одного элемента, и (#)
возвращает длину. Строго говоря, (:[])
является (:)
который занимает []
в качестве первого аргумента.
следовательно (:[]) "hoobar" = "hoobar":[] = ["hoobar"]
и применяя (#)
к нему возвращается 1.
change_one
Начинается с пустого списка []
и перебирает список со вставкой 1 вперед.
foldr insert_one [] ["apple","banana"]
= foldr insert_one [1] ["apple"]
= foldr insert_one [1,1] []
Я не знаю Миранду слишком хорошо, но на основе Хаскелла (я считаю, что различия между ними здесь будут незначительными, только с #
будучи унарным оператором для длины списка, являясь единственным полусущественным и с ||
является синтаксисом комментария): .
Состав функции:
(p . q) x = p (q x)
|| also can be written as:
p . q = \x -> p (q x)
Композиция функций является ассоциативной операцией, поэтому p . (q . r)
знак равно (p . q) . r
знак равно p . q . r
,
Используя эту информацию, мы можем расширить ее с помощью определения .
:
g = (foldr (+) 0) . (foldr ((:) . ((#) . (:[]))) []) || Original definition
g list = foldr (+) 0 (foldr ((:) . ((#) . (:[]))) [] list)
g list = foldr (+) 0 (foldr (\x -> (:) (((#) . (:[])) x)) [] list)
g list = foldr (+) 0 (foldr (\x -> (:) ((\y -> (#) ((:[]) y)) x)) [] list)
Это можно исправить еще немного:
g list = foldr (+) 0 (foldr (\x -> (:) ((\y -> (#)(y:[])) x)) [] list) || More conventional operator syntax for the innermost `:`
g list = foldr (+) 0 (foldr (\x -> (:) ((#)(x:[]))) [] list) || Innermost lambda was applied to x. Substitute y for x.
g list = foldr (+) 0 (foldr (\x -> (:) ((#)([x]))) [] list) || Apply innermost `:`
g list = foldr (+) 0 (foldr (\x -> (:) #[x])) [] list) || Remove unnecessary parentheses
g list = foldr (+) 0 (foldr (\x acc -> (:) (#[x]) acc) [] list) || Explicitly write implicit argument. This particular step is called eta-expansion
g list = foldr (+) 0 (foldr (\x acc -> (:) 1 acc) [] list) || #[x] is always 1, no matter what x is
g list = foldr (+) 0 (foldr (\x acc -> 1 : acc) [] list) || More conventional syntax for `:`
Также обратите внимание, что foldr
не применяется +0
к каждому элементу, как вы указали в вопросе. foldr op z (a:b:c:[])
становится op a (op b (op c z)))
(a:b:c:[]
это еще один способ написать [a,b,c]
). Я всегда думал, что эта диаграмма полезна, чтобы понять, что:
Кроме того, наиболее вероятной причиной возникновения ошибки при ее непосредственном вызове является то, что p . q x
это не то же самое, что (p . q) x
,