[英]Ranges in Haskell (GHCi)
我正在讀“ 為了好大學而學習你的哈斯克爾” 。 他的例子 [2,2..20]
和[3, 6..20]
工作正常,但我得到了三個奇怪的結果:
[17, 1..171]
得到空列表。 [17, 17..171111]
重復數字17
直到我中斷GHCi。 take 54 [171, 234..]
和take 54 [171, 244..]
take 54 [171, 234..]
之間有一個奇怪的區別:
ghci> take 54 [171, 234..] [171,234,297,360,423,486,549,612,675,738,801,864,927,990,1053,1116,1179,1242,1305,1368,1431,1494,1557,1620,1683,1746,1809,1872,1935,1998,2061,2124,2187,2250,2313,2376,2439,2502,2565,2628,2691,2754,2817,2880,2943,3006,3069,3132,3195,3258,3321,3384,3447,3510] ghci> take 54 [171, 244..] [171,244,317,390,463,536,609,682,755,828,901,974,1047,1120,1193,1266,1339,1412,1485,1558,1631,1704,1777,1850,1923,1996,2069,2142,2215,2288,2361,2434,2507,2580,2653,2726,2799,2872,2945,3018,3091,3164,3237,3310,3383,3456,3529,3602,3675,3748,3821,3894,3967,4040]
為什么?
你有略微偏離范圍的含義。 Haskell范圍語法是四個方面之一: [first..]
, [first,second..]
, [first..last]
, [first,second..last]
。 來自Learn You A Haskell的例子是
ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
ghci> [3,6..20]
[3,6,9,12,15,18]
請注意,在第一種情況下,列表按兩次計數,而在第二種情況下,列表按三次計算。 那是因為第一和第二項之間的差異分別是兩個和三個。 在你的語法中,你正在嘗試編寫[first,step..last]
來獲取列表[first,first+step,first+2*step,...,last]
; 但是,這樣的范圍的步長實際上是前兩個數字之間的差異。 沒有第二個元素,步長總是一個; 如果沒有最終元素,列表將永遠持續(或直到達到類型的最大/最小元素)。
因此,讓我們看看你的三個例子:
[17,1..171] == []
。 由於你指定17,1
,Haskell看到列表的前兩個元素應該是17和1,所以你必須按-16
計數。 在這種情況下,Haskell想要在元素小於最后一個元素時立即停止---但它們以這種方式開始,因此不會產生任何元素。 要按1計算,你需要[17,18..171]
(列表的前兩個元素是17和18),或者只是[17..171]
。
[17, 17..171111] == repeat 17
。 這個很有趣。 由於列表中的前兩個元素都是17
,因此Haskell確定您必須按零計數 - 並且它將很樂意繼續計數,直到結果超過171111
。 當然,當計數為零時,這將永遠不會發生,因此您將得到七個無限的列表。 如果你認為更清楚,你需要[17,34..171111]
或[17,17+17..171111]
。
take 54 [171,234..]
對陣 take 54 [171,244..]
。 我不確定你在這里期待什么行為,但是他們每個人都在做什么與上面相同:第一個返回一個54個整數的列表,從171
開始,計數234 - 171 = 63
; 第二個返回一個54個整數的列表,從171
開始,按244 - 171 = 73
計數。 每個列表都無限遠(或者至少直到maxBound
,如果列表是有限的Ints
而不是任意大的Integers
),所以你只需要前五十四個元素。
關於范圍語法意味着什么范圍(它被翻譯成Enum
類型類中的函數)的一些更細微的細節,包括對浮點數范圍的略微驚人的行為, hammar對另一個問題有一個很好的答案 。
那么,這些操作的語義與你期望的有點不同。 構造[a,b..c]
實際上只是enumFromThenTo abc
語法糖,其行為有點像這樣:
計算d = b - a
。 [a,b..c]
的輸出是[a,a+d,a+d+d,a+d+d+d,...]
。 這是重復的,直到a+n*d > c
,如果d
和c - a
有不同的符號(在這種情況下,列表將是無限的,所以沒有輸出),或者直到達到maxBound
或minBound
,然后輸出結束。 (當然,這是以不同的方式實現的,因為我們在這里使用了Enum
任意實例)。
所以[1,3..10]
變為[1,3,5,7,9]
並且自[17, 17..171111]
17 - 17 = 0
, [17, 17..171111]
得到[17,17+0,17+0+0...]
通過這個稍微復雜的規則, [17, 1..171]
產生空列表。
另外: [x,y..]
是使用函數enumFromThen xy
,其行為與enumFromThenTo
類似,只是沒有邊界條件,所以如果你的Enum
是無限的,那么結果列表也是如此。
我也對這種行為感到有些驚訝,所以我寫了一個對我來說感覺更自然的范圍函數(也許對你來說也是如此):
range step start end = takeWhile (<=end) $ iterate (+step) start
引用你的例子:
按17分從1到171計算
由range 17 1 171
,產生[1,18,35,52,69,86,103,120,137,154,171]
從17年到1711111年由17歲計算
由range 17 17 1711111
,產生[17,34,51,68,85, ...
我也對本教程感到困惑:本教程使用了單詞step,沒有解釋,在我看來並不是我認為的一步。 然后它顯示了很容易被誤解的例子。 因為[2,4..20]
看起來像是從4開始的第2步。
線索在輸出中:
ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
如果你仔細看(我沒有)。 它意味着從2開始,下一個是4,從(4 - 2)開始隱式步驟,繼續輸出數字,步長為2到最多20。
"ghci>" [1,6..20]
[1,6,11,16]
注意20不輸出,因為16 + 5大於20
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.