Хаскелл — Неожиданный результат от вложенного генератора в понимании списка

#haskell

Вопрос:

В этом выражении x должно быть на 1 единицу больше, чем n. Однако результирующий список состоит в основном из отрицательных чисел.

 f :: [Integer]
f = [x-n | x <- [1,2..], n <- [0,1..]]
 

Вывод из take 10 f :

 [1,0,-1,-2,-3,-4,-5,-6,-7,-8]
 

Я ожидал, что это будет список из 1, и я не уверен, почему это не так.

Спасибо.

Комментарии:

1. Я понимаю [1,0,-1,-2,-3,-4,-5,-6,-7,-8] , вы уверены, что результат действительно тот, который вы получаете?

2. @Noughtmare Вы правы, я уже отредактировал вопрос. Спасибо.

3. @Мириам: [0,1..] это бесконечный список, содержащий 0 , -1 , -2 , -3 ,… Поэтому он никогда не будет использовать второй пункт x <- [1, 2 ..] списка.

4. Мне непонятно, почему вы ожидаете список из них. Можете ли вы объяснить, чего вы хотите достичь?

5. @WillemVanOnsem Я пытаюсь взять каждый элемент x, минус каждый элемент n. Возможно, я неправильно понимаю выражение?

Ответ №1:

В вашем определении для каждого значения x каждое значение n сопряжено с ним. Например, рассмотрим

 g :: [Integer]
g = [ (x, n) | x <- [1,2..4], n <- [0,1..3]]
 

На выходе получается

 [(1,0),(1,1),(1,2),(1,3),(2,0),(2,1),(2,2),(2,3),(3,0),(3,1),(3,2),(3,3),(4,0),(4,1),(4,2),(4,3)]
 

Если вы хотите, чтобы элементы x и n были соединены в пары по их соответствующим позициям, попробуйте zip :

 f1 = [x-n | (x, n) <- zip [1,2..] [0,1..]]
 
 > take 10 f1
[1,1,1,1,1,1,1,1,1,1]
 

Ответ №2:

Код, который вы даете

 f :: [Integer]
f = [x-n | x <- [1,2..], n <- [0,1..]]
 

является ли императив эквивалентным

 for x in range(1, inf):
    for n in range(0, inf):
        return x - n
 

так ясно, x это 1 сначала, а затем он петляет n до бесконечности. Таким образом, вы получаете 1 - 0 , 1 - 1 1 - 2 , и т. Д…

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

 zipWith (-) [1..] [0..] -- this is equal to the infinite list [1,1,1,1,...]
 

Ответ №3:

Как упоминает @Willem Van Onsen в комментариях, ваше понимание сводится к вложенному циклу, где внешний цикл на x никогда не переходит к следующей итерации. То, что вы видите, таким образом (1 — n) для каждого значения n.

Что вам, кажется, нужно, так это соединить два списка вместе, а затем выполнить вычитание, вот так:

 f = map (x -> (fst x) - snd x) $ zip [1,2..] [0,1..]
 

Это позволит взять элементы из каждого списка попарно и выполнить вычитание, чтобы получить ответ 1, как вы ожидаете.