2014-01-27 2 views
6

У меня есть список целых чисел на основе букв. Например:Складывание в Haskell с использованием нескольких функций

let charlist = map (ord) "ABCDEF" 

charlist будет выглядеть следующим образом:

[65,66,67,68,69,70] 

У меня есть список из трех функций: (+), (-) и (*). Список в этом примере выглядит следующим образом

let funclist = [(+), (-), (*)] 

Я хочу, чтобы применить функции в порядке между элементами в charlist (если есть больше «пространства» в charlist чем есть элементы funclist, вы начать с начала funclist) и вычислить окончательное значение слева направо, как в следующем:

s = ((((((65) + 66) - 67) * 68) + 69) - 70) 

Я думал об использовании foldl, б ut foldl, похоже, работает только с одной функцией. Есть ли другой способ сделать это? Я хотел бы обобщить весь этот процесс в одной функции, если это возможно, хотя это не требование.

+1

Я предлагаю вам попробовать реализовать это с использованием сопоставления шаблонов и явной рекурсии. Когда у вас есть решение, которое работает, еще есть время, чтобы понять, подходит ли он шаблону для функции более высокого порядка, например 'foldl'. – kosmikus

+1

Вы можете использовать 'цикл' для реализации функции, которую вы начинаете с начала« funclist », когда у вас заканчиваются элементы. – kosmikus

ответ

6

Хотя foldl может применяться только одну функцию, вы можете иметь различные функции в данных. Хитрость прикрепить к вашим данным соответствующие функции +, - или *. Ты сошел с правого начала:

funclist = [(+), (-), (*)] 

Но давайте прямо сейчас сделать бесконечную версию приведенного выше списка, как [(+), (-), (*), (+), (-), (*)...]

infinite_funclist = cycle funclist 

Позволяет назначить номера мы собираемся свернуть здесь.first в этом случае 65, rest является [66..70]

(first:rest) = [65..70] 

Теперь мы пронестись вместе rest и infinite_funclist получить [(66,(+)), (67,(-)), (68,(*)), (69,(+)), (70,(-))]. Мы начинаем с first, и для каждого нового элемента, мы применяем операцию, во второй части текущего кортежа к текущему значению и первой части, как так:

result = foldl' (\acc (v, f) -> acc `f` v) first (zip rest infinite_funclist) 

Если мы хотим напечатать результат мы может сделать это следующим образом:

main = print result 

(Ссылка на код here)

+0

Спасибо за это! Это прекрасно работает. Прямое решение с хорошим объяснением, что позволяет даже для новичков, подобных мне, понять. ;) – Saser

4

По существу, это застежка-молния, а не складка. Nicest бы

> zipWith ($) (цикл funclist) charlist
[(65+), (66-), (67 *), (68+), (69-), (70+)]

, но из-за левой ассоциативности и начального элемента нам нужно сделать, это немного более многословным

> пусть ptApplied = хвост. zipWith (($). флип) (цикл funclist) $ charlist
ptApplied = [(+66), (вычесть 67), (* 68), (+69), (70) вычесть]

только тогда приходит складка

> foldl»(флип ($)) (глава charlist) ptApplied

0

Кто говорит, что вы не можете "передать переменные" в foldl?

Prelude> let f = [(+), (-), (*)] 

Prelude> let c = [65,66,67,68,69,70] 

Prelude> foldl (\(a,i) val -> ((f!!(mod i 3)) a val,i + 1)) (head c,0) (tail c) 
(4351,5) 
1

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

Prelude Data.List> snd $ foldl' (\(f:fs, acc) x -> (fs, f acc x)) 
           ((+):cycle funclist, 0) [65,66,67,68,69,70] 
4351 

(я вставил дополнительный (+) на передней funclist так, что «реальный» плюс приходит от 65 до 66 лет, а не между начальным аккумулятором 0 и 65)

+0

Мне нравится ваш ответ; это помогло упростить мою идею. –

 Смежные вопросы

  • Нет связанных вопросов^_^