Я пытаюсь и не могу найти эту traverse
функцию Data.Traversable
. Я не вижу в этом смысла. Поскольку я пришел из императивного фона, может ли кто-нибудь объяснить мне это с точки зрения императивного цикла? Псевдокод был бы очень признателен. Спасибо.
99
Ответы:
traverse
то же самое, что иfmap
, за исключением того, что он также позволяет запускать эффекты, пока вы перестраиваете структуру данных.Взгляните на пример из
Data.Traversable
документации.Functor
ЭкземплярTree
будет:Он перестраивает все дерево, применяя
f
к каждому значению.Traversable
Экземпляр почти то же самое, за исключением того, что конструкторы вызываются в аппликативном стиле. Это означает, что мы можем иметь (побочные) эффекты при восстановлении дерева. Аппликатив почти такой же, как и монады, за исключением того, что эффекты не могут зависеть от предыдущих результатов. В этом примере это означает, что вы не можете сделать что-то отличное от правой ветви узла, например, в зависимости от результатов восстановления левой ветви.По историческим причинам,
Traversable
класс также содержит одноместную версиюtraverse
называетсяmapM
. Для всех намерений и целейmapM
это то же самое, что иtraverse
- он существует как отдельный метод, потому чтоApplicative
только позже стал суперклассомMonad
.Если бы вы реализовали это на нечистом языке, это
fmap
было бы то же самое, что иtraverse
, поскольку нет способа предотвратить побочные эффекты. Вы не можете реализовать его как цикл, поскольку вам нужно рекурсивно перемещаться по структуре данных. Вот небольшой пример того, как я бы сделал это в Javascript:Такая реализация ограничивает вас теми эффектами, которые позволяет язык. Если вам нужен недетерминизм (который является экземпляром списка моделей Applicative), а в вашем языке он не встроен, вам не повезло.
источник
Functor
части, которая не является параметрической. Значение состояния вState
, сбой вMaybe
иEither
, количество элементов в[]
, и, конечно, произвольные внешние побочные эффекты вIO
. Меня не интересует это как общий термин (например,Monoid
функции, использующие «empty» и «append», концепция более универсальна, чем этот термин сначала предполагает), но он довольно распространен и достаточно хорошо служит цели.ap
зависимость от предыдущих результатов. Я соответствующим образом изменил это замечание.traverse
превращает вещи внутри aTraversable
в aTraversable
вещей "внутри" anApplicative
, учитывая функцию, которая делаетApplicative
s из вещей.Давайте использовать
Maybe
asApplicative
и перечислить какTraversable
. Для начала нам понадобится функция преобразования:Итак, если число четное, мы получаем его половину (внутри a
Just
), иначе мы получаемNothing
. Если все идет "хорошо", это выглядит так:Но...
Причина в том, что
<*>
функция используется для построения результата, и когда один из аргументов естьNothing
, мыNothing
возвращаемся.Другой пример:
Эта функция генерирует список длины
x
с содержимымx
, напримерrep 3
=[3,3,3]
. Что в результатеtraverse rep [1..3]
?Мы получаем частичные результаты
[1]
,[2,2]
и[3,3,3]
использованияrep
. Теперь семантика списков , какApplicatives
это «принимать все комбинации», например ,(+) <$> [10,20] <*> [3,4]
есть[13,14,23,24]
.«Все комбинации»
[1]
и[2,2]
составляют два раза[1,2]
. Все комбинации два раза[1,2]
и[3,3,3]
шесть раз[1,2,3]
. Итак, у нас есть:источник
fac n = length $ traverse rep [1..n]
liftA2 (,)
чем на использование более общей формыtraverse
.Я думаю, что это легче всего понять в терминах
sequenceA
, которыеtraverse
можно определить следующим образом.sequenceA
объединяет элементы структуры слева направо, возвращая структуру той же формы, содержащую результаты.Вы также можете думать об
sequenceA
изменении порядка двух функторов на противоположное, например, переход от списка действий к действию, возвращающему список результатов.Таким образом,
traverse
берется некоторая структура и применяетсяf
для преобразования каждого элемента в структуре в некоторый аппликатив, а затем он упорядочивает эффекты этих аппликативов слева направо, возвращая структуру той же формы, содержащую результаты.Вы также можете сравнить его с
Foldable
, который определяет связанную функциюtraverse_
.Таким образом, вы можете видеть, что ключевое различие между
Foldable
иTraversable
заключается в том, что последний позволяет вам сохранить форму структуры, тогда как первый требует, чтобы вы свернули результат в другое значение.Простым примером его использования является использование списка в качестве проходимой структуры и
IO
в качестве аппликатива:Хотя этот пример довольно неинтересный, все становится интереснее, когда
traverse
он используется с другими типами контейнеров или с другими приложениями.источник
sequenceA . fmap
для списков это эквивалентно,sequence . map
не так ли?IO
Тип может использоваться для выражения побочных эффектов; (2)IO
оказывается монадой, что оказывается очень удобным. Монады по существу не связаны с побочными эффектами. Также следует отметить, что есть значение «эффект», которое шире, чем «побочный эффект» в его обычном смысле, включающем чистые вычисления. По этому последнему пункту см. Также Что именно означает «эффективный» .Это вроде как
fmap
, за исключением того, что вы можете запускать эффекты внутри функции сопоставления, которая также меняет тип результата.Представьте список целых чисел , представляющие идентификаторы пользователей в базе данных:
[1, 2, 3]
. Если вы хотите, чтобыfmap
эти идентификаторы пользователей были добавлены к именам пользователей, вы не можете использовать традиционныеfmap
, потому что внутри функции вам нужно получить доступ к базе данных, чтобы прочитать имена пользователей (что требует эффекта - в данном случае с помощьюIO
монады).Подпись
traverse
:С помощью
traverse
, вы можете создавать эффекты, поэтому ваш код для сопоставления идентификаторов пользователей с именами пользователей выглядит так:Также есть функция
mapM
:Любое использование
mapM
можно заменить наtraverse
, но не наоборот.mapM
работает только для монад, тогда какtraverse
является более общим.Если вы просто хотите добиться эффекта и не возвращать какое-либо полезное значение, существуют
traverse_
иmapM_
версии этих функций, обе из которых игнорируют возвращаемое значение из функции и работают немного быстрее.источник
traverse
это петля. Его реализация зависит от структуры данных, по которой нужно пройти. Это может быть список, дерево,Maybe
,Seq
(uence), или что - нибудь , что имеет общий способ пересекаемой через что - то подобное для цикла или рекурсивной функции. Массив будет иметь цикл for, список - цикл while, дерево - либо что-то рекурсивное, либо комбинацию стека с циклом while; но в функциональных языках вам не нужны эти громоздкие команды цикла: вы объединяете внутреннюю часть цикла (в форме функции) со структурой данных более прямым и менее подробным образом.С
Traversable
классом типов вы, вероятно, могли бы написать свои алгоритмы более независимыми и универсальными. Но мой опыт говорит, чтоTraversable
это обычно используется только для простого приклеивания алгоритмов к существующим структурам данных. Довольно хорошо, что не нужно писать похожие функции для разных типов данных.источник