2013-09-21 1 views
2

Я новичок в haskell, и я пытаюсь изучить haskell. Я пытаюсь создать простые функции, такие как «дублировать», как можно более подробно. Я уже сделал «дубликат» с совпадением рисунков и защитой. Я не могу заставить его работать с помещением внутри понимания списка. Я могу себе представить, что внутри понимания списка не идеально, но меня больше интересует, почему он не работает =].Haskell: Пусть внутри List comprehension неожиданный результат

Мой вопрос: почему duplicate 'производит [Int] и duplicate' 'производит [[Int]] и возможно ли даже создать [Int] с допуском внутри понимания списка?

Спасибо за ваше время и помощь :).

--list comprehension 
duplicate' xs = [y | x <- xs, y <- [x,x]] 
input => [1,2,3,4] 
output => [1,1,2,2,3,3,4,4] 
expected? => yes 

--list comprehension with let 
duplicate'' xs = [y | x <- xs, let y = [x,x]] 
input => [1,2,3,4] 
output => [[1,1],[2,2],[3,3],[4,4]] 
expected? => yes 
+0

'[у | x <- xs, y = [x, x]] '==' [[x, x] | x <- xs] 'вы видите, это не то, что вы хотели –

ответ

5

<- и let просто означают разные вещи.

Когда вы пишете y <- [x,x], вы говорите «дайте y каждое из значений внутри списка [x,x] в свою очередь».

Когда вы пишете let y = [x,x], вы говорите «дайте y значение [x,x]».

4

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

[ y | x <- xs, let y = [x,x] ] ≡ [ [x,x] | x <- xs ] 

Любое такое выражение лишь один <- имеет, таким образом, форму

[ f x | x<-xs ] 

что эквивалентно map f xs. Таким образом, список результатов должен всегда иметь ту же длину, что и xs, что делает невозможным достижение желаемого поведения duplicate: если вы хотите иметь дубликаты, вам необходимо инкапсулировать их как внутренние списки, чтобы они не учитывали больше элементов.

Чтобы объединить эти вложенные списки обратно в "плоский" один, вы можете использовать тот факт, что списки являются монада:

join :: Monad m => m (m a) -> m a 

Prelude Control.Monad> присоединиться к [[1,1], [2,2]]
[1,1,2,2]

Теперь join в действительности, как теоретики категории предпочитают определять монады, но, как вы знаете, Haskell делает это немного по-другому:

(>>=) :: Monad m => m a -> (a -> m b) -> m b 
a >>= f = join (fmap f a) 

или, как это на самом деле определяется наоборот,

join a = a >>= id 

Положи в модифицированном let версии duplicate:

join [y | x <- xs, let y = [x,x] ] 
    ≡ [y | x <- xs, let y = [x,x] ] >>= id 
    ≡ map (\x -> [x,x]) xs >>= id 
    ≡ xs >>= id . (\x -> [x,x]) 
    ≡ xs >>= (\x -> [x,x]) 
    ≡ do { x<-xs; [x,x] } 
    ≡ do { x<-xs; y<-[x,x]; return y } -- by the monad laws 

Теперь выражение do { a<-p; b<-q; ... return x } является monad comprehension, обобщение перечня понятий.Его можно переписать [x | a<-q, b<-q, ...]. По нашей проблеме

join [y | x <- xs, let y = [x,x] ] ≡ [y | x<-xs, y<-[x,x]] 

, с которого вы начали. При этом необходимо использовать два <- с чистым пониманием списка.

Конечно, вы могли использовать также подведенный, в любой точке ...

[y | x<-xs, y<-[x,x]] 
    ≡ [y | x<-xs, let z=[x,x], y<-z] 
    ≡ [a | x<-xs, let z=[x,x], y<-z, let a=y] 
    ≡ [a | x<-xs, let z=let w=[let q=x in q, let r=x in r] in w, y<-z, let a=y] 
    ≡ ... 
+0

Правильно ли я понимаю, что concat является специализированным соединением для списков? (Или, может быть, наоборот: join для списка monad реализовано с помощью concat?) – kqr

+0

@kqr 'join' (определяется с помощью' (>> =) ', который определяется' foldr' для '[a]') и 'concat' (определяется с помощью' foldr') определяются отдельно, но оба делают то же самое для списков. – wit

+0

Спасибо за подробное объяснение, помогли много. – BARJ