2015-03-18 2 views
4

Python's enumerate в списках могут быть указаны как zip [0..]. Я смотрел Control.Lens.Traversal и Control.Lens.Indexed, но я не мог понять, как использовать объективы, чтобы обобщить это на любой разумный контейнер (я смущенно говорю «Traversable»).Как использовать объектив в Haskell для дублирования перечисления Python?

Я предполагаю, что itraverse или itraverseOf является ключевым.

+0

Я считаю, 'lens' предлагает индексированную карту, но я не уверен. Я знаю, что если вы используете 'Data.Sequence', гораздо эффективнее использовать' mapWithIndex (,) ', чем любой тип обхода. – dfeuer

+1

'itraverse' конечно делает то, что вы хотите. Какую трудность вы используете? – Carl

+0

@Carl Моя наивная интуиция 'itraverse (,)" abcd "' жалуется на отсутствие экземпляра моноида для Int. Это также похоже на использование '(,)', поскольку первый аргумент дает мне неправильный тип вывода, а именно '(Int, t a)', а не 't (Int, a)'. –

ответ

8

Если вы используете контейнер, который является экземпляром FunctorWithIndex, то вы можете просто использовать imap (,):

> imap (,) "abc" 
[(0,'a'),(1,'b'),(2,'c')] 

Но если индекс не является позицией, это не сработает:

> let m = Map.fromList [('a', "foo"), ('b', "bar"), ('c', "foobar")]) 
> imap (,) m 
fromList [('a',('a',"foo")),('b',('b',"bar")),('c',('c',"foobar"))] 

Вместо этого вы можете использовать traversed, который является индексированным обходом, где индекс представляет собой порядок, в котором отображаются элементы. Это можно использовать для всего, что есть Traversable. Вместо того, чтобы использовать imapiover traversed (который так же, как imapOf но это было устаревшее):

> iover traversed (,) "abc" 
[(0,'a'),(1,'b'),(2,'c')] 

> iover traversed (,) m 
fromList [('a',(0,"foo")),('b',(1,"bar")),('c',(2,"foobar"))] 
+0

Спасибо. Это то, что я искал. –

6

Одно решение было бы использовать State монаду с traverse, так как он также Applicative:

enumerate :: (Integral n, Traversable t) => t a -> t (n, a) 
enumerate t = evalState (traverse go t) 0 
    where 
     go a = do 
      i <- get 
      modify (+1) 
      return (i, a) 
+1

В стиле «Аппликативный», кстати, 'go a = (\ i -> (i, a)) <$> get <* modify (+1)'. Могу ли я предложить '(Traversable t, Integral n) => t a -> t (n, a)'? – dfeuer

+0

@dfeuer Конечно, я просто ленился. Вы могли бы даже расслабить его дальше до «Num», если бы вы действительно этого хотели. – bheklilr

+0

Это решение, которое я, вероятно, использовал бы нормально (хорошо, глядя на мой код, он более похож на модификацию @dfeuer, за исключением использования разделов tuple и 'succ'. Я ищу конкретный ответ на объектив, хотя частично чтобы расширить мое понимание пакета. –

3

Вы игнорируя Applicative контекст на itraverse. Вам нужно что-то для его работы. Но что-то может быть скучным, например, Identity.

imap f = runIdentity . itraverse (\i a -> return (f i a)) 

И тогда вы получите то, что вы ищете:

> imap (,) [1,2,3] 
[(0,1),(1,2),(2,3)] 
+0

А, есть более удобные вещи, такие как ответы в чхалмере , – Carl