簡體   English   中英

Haskell 列表推導式無限列表問題

[英]Haskell List comprehensions infinite list problem

我正在嘗試學習 Haskell 和理解列表,但找不到解決方案:

mylist = [x*y | x <- [1..], y <- [1..]]

經過我的試驗,結果是這樣的

mylist = [1,2,3,4,5,...]

因為在列表推導式中, x取值為1 ,然后y重復更改值。

但我的目標是完成不同的任務,從而得到以下結果:

mylist = [1,2,2,4,3,3,6.....]

我的意思是我想要混合組合而不是每個組合,因為我有一個嚴重的問題來獲得合適的結果。

我會舉一個更具體的例子。

我想要一個包含這種形式的所有數字的列表:

num = 2^x * 3^y 

xy必須取所有值>= 0

我的方法如下:

powers = [2^x * 3^y | x <- [0..], y <- [0..]]

但是這樣我只取 3 的冪,因為x始終為 0。

我試過這個

multiples = nub (merge (<=) powers2 powers3)
powers3 = [2^x * 3^y | x <- [0..], y <- [0..]]
powers2 = [2^x * 3^y | y <- [0..], x <- [0..]]

以便合並不同的但同樣,值 6,12 等。 丟失了 - 結果是這樣的:

mylist = [1,2,3,4,8,9,16,27,32,64,81...]

你展示的代碼,

multiples = nub (merge (<=) powers2 powers3)
powers3 = [2^x * 3^y | x <- [0..], y <- [0..]]
powers2 = [2^x * 3^y | y <- [0..], x <- [0..]]

相當於

powers3 = [2^x * 3^y | x <- [0], y <- [0..]]
        = [2^0 * 3^y | y <- [0..]]
        = [3^y | y <- [0..]]
powers2 = [2^x * 3^y | y <- [0], x <- [0..]] 
        = [2^x * 3^0 | x <- [0..]]
        = [2^x | x <- [0..]]

所以你只產生23的冪,沒有任何混合倍數。 因此,保證流中沒有重復項,並且沒有必要使用nub 當然,它是不完整的。

但讓我們換個角度來看。 在評論中建議使用這些數字創建一個 2D 網格:

mults23_2D = [[2^x * 3^y | y <- [0..]] | x <- [0..]]
{-
   1   3   9   27  81  ...
   2   6  18   54  ...
   4  12  36  108  ...
   8  24  72  ...
  16  ...
  .......     
-}

現在我們正在到達某個地方。 至少現在沒有一個被跳過。 我們只需要了解如何將它們連接成一個有序的、遞增的數字流。 簡單的concat當然不行。 我們需要按順序合並它們。 一個眾所周知的函數merge做到這一點,前提是參數已經排序,增加列表。

生成的每一行都已經按遞增順序排列,但它們的數量是無限多的。 不要害怕, foldr可以做到。 我們定義

mults23 = foldr g [] [[2^x * 3^y | y <- [0..]] | x <- [0..]]
  -- foldr g [] [a,b,c,...] == a `g` (b `g` (c `g` (....)))
 where
 g (x:xs) ys = 

這里有點棘手。 如果我們定義g = merge ,我們將有一個失控的遞歸,因為每個merge都想知道它的“右”(第二個)參數流的頭元素。

為了防止這種情況,我們立即生成最左邊的元素。

                x : merge xs ys

就是這樣。

工具使用

我需要一個無限的笛卡爾積函數。 無限函數必須采用表格的對角線。 對角線遍歷的對模式是

0 0 – 0 1, 1 0 – 0 2, 1 1, 2 0 – 0 3, 1 2, 2 1, 3 0

我喜歡對稱性,但模式是用第一個數字向前計數,用第二個數字向后計數,當用無限函數表示時是

diag2 xs ys = [ (m,n) | i<- [1..], (m,n) <- zip (take i xs) (reverse.take i $ ys) ]

無限代只是取任何數量來工作。 可能重要的是,取一個對角線或三角形數來表示一個完整的集合。 revt n根據您的輸入生成一個三角形數。 如果你想要 25 個元素, revt 25將返回revt 25 tri 7將返回 28 的參數take revttri

tri n = foldl (+) 1 [2..n]
revt n = floor (sqrt (n*2))

在你學會前 10 個左右的三角形數之前,制作和使用taket是很好的。

taket n xs = take (tri $ revt n) xs

現在,有了一些工具,我們將它們(主要是 1)應用到一個問題上。

[ 2^a * 3^b | (a,b) <- sort.taket 25 $ diag2 [0..] [0..]]

[1,3,9,27,81,243,729, 2,6,18,54,162,486, 4,12,36,108,324, 8,24,72,216, 16,48,144, 32,96, 64]

而且是對角線。 第一組7長,第二組6長,倒數第二組2長,最后一組1長。 revt 25revt 25 tri 7是 28 輸出列表的長度。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM