LINUX.ORG.RU

foldr/foldl и поиск ключа в массиве

 ,


0

1

Учу хаскелль. В учебнике разбирается задача поиска значения по ключу в ассоциативном массиве [(k,v)]. Есть два примера кода:

findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
findKey key [] = Nothing
findKey key ((k,v):xs) = if key == k
then Just v
else findKey key xs
findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
«Ага, лучше использовать первый, так как он прервётся как только найдёт искомый ключ, а второй пройдёт по всему массиву в любом случае» - думаю я. И тут-же в следующем абзаце автор говорит, что использовать лучше второй вариант. Почему я не прав?

Второй способ понятней. Тут имеется ввиду, что foldr наглядней, ксли не ошибаюсь это hs в реальном мире. Лучше кончено использовать готовую lookup.
--
Fluttershy.

anonymous
()
Ответ на: комментарий от ugoday
foldr k z = go
          where
            go []     = z
            go (y:ys) = y `k` go ys

Значение аккумулятора возвращается только после прохода всего списка. И ленивость здесь не работает.

Dragon59 ★★
() автор топика
Ответ на: комментарий от Dragon59

k - не может быть ленивой ф-ей?

// по сабжу: имхо, первый читабельнее (только guard заместо if), lookup так и сделан

anonymous
()

Потому что foldr это такой шаблон, и лучше использовать шаблоны.

aptyp ★★★★
()
Ответ на: комментарий от anonymous

ксли не ошибаюсь это hs в реальном мире

Нет, Learn You a Haskell for Great Good
Спасибо за помощь, флатти :)

Dragon59 ★★
() автор топика

«Ага, лучше использовать первый, так как он прервётся как только найдёт искомый ключ, а второй пройдёт по всему массиву в любом случае»

т.е. 2-ой findKey не выполнится на бесконечном списке? Советую проверить.

qnikst ★★★★★
()

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

Mihai-gr
()
Ответ на: комментарий от Mihai-gr

Безымянная функция во втором варианте игнорирует второй аргумент, если значение найдено,

а можно обоснование?

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Можешь рассказать, кинуть почитать на тему «почему foldr» не вываливается и выполняется. Что-то тут не совсем чисто.

> let a = [(x, x*x) | x <- [1..]]
> let b = foldr (\(k,v) acc -> if 5 == k then Just v else acc) Nothing a
> b
Just 25

anonymous
()
Ответ на: комментарий от anonymous

Но естественно падает, когда ключ не найден, единственное, что приходит в голову: что оптимизируется вызов.

anonymous
()
Ответ на: комментарий от qnikst

Можешь проверить. Провел экспиремент, выходит, что foldr лениво обрабатывает список и так же гипотетически заполянет аккумулятор. Вот иллюстрация:

Prelude> let a = [x | x <- [1..]]
Prelude> let b = foldr (\int acc -> if int == 4 then int:acc else acc) [] a
Prelude> take 1 b
[4]
Prelude> let b = foldr (\int acc -> if int == 4 then acc++[int] else acc) [] a
Prelude> take 1 b
CInterrupted.
--
Fluttershy

anonymous
()

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

Miguel ★★★★★
()
Ответ на: комментарий от Dragon59

Значение аккумулятора возвращается только после прохода всего списка.

Нет, если функция k не требует вычисления второго аргумента.

Miguel ★★★★★
()

второй пройдёт по всему массиву в любом случае

> foldr (\e a -> e) 0 [1 ..]
1
> let findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
> let inf = (1, 2) : inf
> take 10 inf
[(1,2),(1,2),(1,2),(1,2),(1,2),(1,2),(1,2),(1,2),(1,2),(1,2)]
> findKey 1 inf
Just 2

если функции k передаваемая foldr ленива относительно аккумулятора, то

            go (y:ys) = y `k` go ys

спокойно вернёт значение без дальнейшего вычисления go для ys.

quasimoto ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.