Использую ли я обоснованные рациональные рассуждения об определении фильтра в терминах Foldr?
Хорошо, это определение функции фильтра с использованием foldr:
myFilter p xs = foldr step [] xs
where step x ys | p x = x : ys
| otherwise = ys
так, например, скажем, у меня есть эта функция:
myFilter odd [1,2,3,4]
так будет:
foldr step [] [1,2,3,4]
и это будет
step 1 (foldr step [] [2,3,4])
и это будет
step 1 (step 2 (foldr step [] [3,4]))
и это будет
step 1 (step 2 (step 3 (foldr step [] [4])))
и это будет
step 1 (step 2 (step 3 (step 4 (foldr step [] []))))
а также foldr step [] []
является []
так:
step 1 (step 2 (step 3 (step 4 [])))
теперь мы на самом деле попасть в step
функция.
вот определение step
внутри myFilter
функция сверху:
step x ys | p x = x : ys
| otherwise = ys
Кроме того, я напоминаю вам, что p
на самом деле odd
функция в нашем примере.
ну опять мы здесь
step 1 (step 2 (step 3 (step 4 [])))
а также
x = 4
в самом внутреннем step
, а также 4
не странно, поэтому мы возвращаемся ys
, который []
так что теперь мы получаем это:
step 1 (step 2 (step 3 []))
сейчас, в самом внутреннем step
, x = 3
, а также 3
странно, поэтому мы возвращаемся x:ys
, который 3 : []
, который [3]
и теперь мы получаем:
step 1 (step 2 [3])
а теперь во внутреннем step
, x = 2
, а также 2
не странно, поэтому мы возвращаемся ys
, который [3]
Итак, теперь мы получим:
step 1 [3]
и сейчас, x = 1
, а также 1
странно, поэтому мы возвращаемся x : ys
, который 1 : [3]
, который [1,3]
,
Конец:-).
я прав во всех своих шагах?
большое спасибо:-).
PS определение myFilter
это из книги Real World Haskell, в главе 4.
3 ответа
Это выглядит правильно для меня при первом прочтении.
Однако важно помнить, что для того, чтобы добиться ленивой оценки, Haskell будет смотреть на вещи иначе. Другими словами, реальная последовательность больше похожа
step 1 (step 2 (step 3 (step 4 [])))
становится
step 1 <block1>
который становится
[1, <block1>]
тогда, если вы попытаетесь извлечь следующий элемент из этого списка, он оценит
[1, step 2 <block2>]
который становится
[1, <block2>]
а затем пытается оценить
[1, step 3 (step 4 [])]
превращается в
[1, step 3 <block3>]
который становится
[1, 3, <block3>]
и т.д. Это заняло у меня некоторое время, чтобы понять. Для меня было нелогичным, что с foldr
кажется, оценивается "наизнанку", но foldl
оценивается извне, что foldr
будет ленивым (что это такое), тогда как foldl
строгое Но если вы думаете об этом так, как я обрисовал выше, это имеет смысл (для меня, во всяком случае).
Просто чтобы расширить ленивый порядок оценки: в основном Haskell всегда сначала оценивает функцию, не глядя на аргументы, пока это не нужно.
Если результат звонка myFilter
используется (например, напечатано), функция будет оценена в следующем порядке:
myFilter odd [1,2,3,4]
Сначала myFilter
Функция оценивается:
foldr step [] [1,2,3,4]
Сейчас foldr
является самой внешней функцией и оценивается:
step 1 (foldr step [] [2,3,4])
Сейчас step
оценивается производя 1
, поскольку 1
странный
1 : foldr step [] [2,3,4]
Теперь первый элемент списка результатов доступен и может использоваться вызывающей функцией. Если вызывающая функция также использует следующие элементы, оценка продолжается с foldr
:
1 : step 2 (foldr step [] [3,4])
Оценка step
теперь не создает никаких новых элементов, так как 2 четное:
1 : foldr step [] [3,4]
Так foldr
снова:
1 : step 3 (foldr step [] [4])
Сейчас оцениваю step
производит 3
:
1 : 3 : foldr step [] [4]
Оценка foldr
;
1 : 3 : step 4 (foldr step [] [])
А также step
еще раз:
1 : 3 : foldr step [] []
в заключение foldr
оценивает пустой список:
1 : 3 : []
На первый взгляд шаги, которые вы предприняли в своем конкретном примере, выглядят корректно в отдельности. Тем не менее, я хотел бы отметить, что оба filter
а также foldr
может быть с пользой применен к бесконечным спискам- что должно указывать на то, что порядок ваших шагов неверен с точки зрения Haskell.