[英]Getting even and odd position of elements in list - Haskell Mutual Recursion
我最近開始學習Haskell。
我在網上找到了這個代碼,它返回列表所有偶數/奇數位置的元素。
它利用了相互遞歸,但我似乎無法理解它是如何在內部工作的。
evens (x:xs) = x:odds xs
evens _ = []
odds (_:xs) = evens xs
odds _ = []
特別是,我不明白該清單如何向前推進並評估所有要素。 即使沒有明確的索引檢查,它如何檢查偶數位置
有人能夠提供洞察力嗎?
首先,讓我們就某事達成一致:我們大多數時候都使用0指數,對吧? 所以,如果我問你列表中第2位的元素是什么
a : b : c : d : []
你會回答c
。 編輯 :如果您不熟悉Haskell表示法,則:
列表構造函數和a : b
表示通過在列表b
前面添加前綴而a
的列表。
繼續到偶數位置的元素, a
和c
將是明顯的答案,而b
和d
將處於奇數位置。 讓我們來看看even
的定義。
evens (x:xs) = x:odds xs
evens [] = []
基本情況很簡單:空列表中的偶數位置沒有元素
歸納案例說明位置0( x
)中的元素處於偶數位置 - 它是 - 並且它還表示列表中偶數位置中的所有其他元素(x:xs)
位於奇數位置列表xs
。 實際上,列表中位置2中的元素(x:xs)
位於列表xs
中的位置1,位置3中位置3中的元素,依此類推; 那有意義嗎?
使用相同的推理,下面列表中奇數位置的元素(x:xs)
是列表xs
中偶數位置的元素,這正是上面odds
的定義。
使用Debug.Trace
查看索引如何隨每次遞歸調用而變化。 trace
函數在返回第二個參數之前將其第一個參數打印到標准錯誤。
import Debug.Trace
evens (x:xs) = x : odds (trace (show (zip [0..] xs)) xs)
evens [] = []
odds (_:xs) = evens (trace (show (zip [0..] xs)) xs)
odds [] = []
main = print (evens "abcdefg")
標准錯誤將顯示
[(0,'b'),(1,'c'),(2,'d'),(3,'e'),(4,'f'),(5,'g')]
[(0,'c'),(1,'d'),(2,'e'),(3,'f'),(4,'g')]
[(0,'d'),(1,'e'),(2,'f'),(3,'g')]
[(0,'e'),(1,'f'),(2,'g')]
[(0,'f'),(1,'g')]
[(0,'g')]
[]
每次進行遞歸調用時,每個原始項目的位置會“移動”一個位置。 例如, g
在原始列表中處於偶數位置,但在每個遞歸調用中從偶數位置交替到奇數位置。
讓我們看一下輸入列表上的evens
樣本評估。 我將使用"abcde"
-recall, String
是字符列表[Char]
的別名,所以這相當於['a', 'b', 'c', 'd', 'e']
或'a' : 'b' : 'c' : 'd' : 'e' : []
。
我們從最初的輸入開始:
evens "abcde"
匹配第一種evens
模式,在結果的開頭添加'a'
並繼續執行列表的其余部分:
evens "abcde"
-------------
-- evens (x : xs) = x : odds xs
-- x = 'a'
-- xs = "bcde"
-- evens "abcde" = 'a' : odds "bcde"
'a' : odds "bcde"
-----------------
匹配第一種odds
模式,忽略'b'
並繼續:
'a' : odds "bcde"
-----------
-- odds (_ : xs) = evens xs
-- xs = "cde"
-- odds "bcde" = evens "cde"
'a' : evens "cde"
-----------
第一種evens
模式,添加'c'
:
'a' : evens "cde"
-----------
-- evens (x : xs) = x : odds xs
-- x = 'c'
-- xs = "de"
-- evens "cde" = 'c' : odds "de"
'a' : 'c' : odds "de"
---------------
第一種odds
模式,忽略'd'
:
'a' : 'c' : odds "de"
---------
-- odds (_ : xs) = evens xs
-- xs = "e"
-- odds "de" = evens "e"
'a' : 'c' : evens "e"
---------
第一種evens
模式,添加'e'
:
'a' : 'c' : evens "e"
---------
-- evens (x : xs) = x : odds xs
-- x = 'e'
-- xs = "" = []
-- evens "e" = 'e' : odds ""
'a' : 'c' : 'e' : odds ""
-------------
現在,最后,第一個odds
模式不匹配,因為空列表[]
與列表構造函數_ : _
不匹配,所以我們落到第二個(默認)模式:
'a' : 'c' : 'e' : odds ""
-------
-- odds _ = []
-- odds "" = []
'a' : 'c' : 'e' : []
--
給出最終結果:
"ace"
基本上這些函數將輸入視為值的“流”並產生流作為結果: evens
消耗一個元素並將其輸出到結果,繼續獲取余數的odds
; 雖然odds
消耗了一個元素並將其丟棄,但剩下的則是evens
。
這不會對索引進行任何計算,因為它沒有 - 它只是遵循列表的結構。 根據定義,列表中的第一個值是偶數索引(當從0開始計數時),因此evens
保留它並獲取余數的奇數索引,而odds
丟棄它並獲取余數的偶數索引。 刪除每個元素會將所有索引向下移動一個 - 也就是說,輸入列表中索引1
處的元素位於輸入尾部的索引0
處:
zip [0..] "abcde" == [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
'a' 'b' 'c' 'd' 'e'
0 1 2 3 4
| | | | |
x / / / /
/ / / /
/ / / /
/ / / /
| | | |
'b' 'c' 'd' 'e'
0 1 2 3
zip [0..] "bcde" == [(0, 'b'), (1, 'c'), (2, 'd'), (3, 'e')]
您還可以使用索引而不是相互遞歸來顯式實現這些函數。 使用列表推導:
evens xs = [x | (i, x) <- zip [0..] xs, even i]
odds xs = [x | (i, x) <- zip [0..] xs, odd i]
或者使用顯式map
和filter
:
evens = map snd . filter (even . fst) . zip [0..]
odds = map snd . filter (odd . fst) . zip [0..]
然后你甚至可以將索引上的謂詞變成一個參數:
indices f = map snd . filter (f . fst) . zip [0..]
evens = indices even
odds = indices odd
我們自己的語言非常強大。 在我自學微積分時,我遇到了極限,直到我讀了一段牛頓中的一句話。 當然,這是用英語寫的。
首先,您對未使用或不需要的索引是正確的。
其次,代碼不知道偶數或奇數之間的區別,你再次對它提出質疑。
最后,我稍微修改了這些,以便在我的實現上正常工作。
evens [x] = [x]; evens (x:xs) = x:odds xs
odds [x] = []; odds (_:xs) = evens xs
他們的工作方式是平均的。 它構建輸出列表。 它接受列表的第一項並使其成為輸出列表的第一項或下一項。 它用列表的其余部分調用賠率。 賠率只是將收到的東西的尾巴返回到均衡。
像尾巴一樣,它會丟棄它收到的第一項。
evens生成一個列表,其中大約一半的項目被丟棄。 賠率幾乎不會產生任何影響,但是關鍵的丟棄。
如果給evens列表[0,1,2,3,4]
則返回從0
開始的所有其他項,即[0,2,4]
。 如果你給evens列表[1,2,3,4]
它返回[1,3]
因為列表以奇數開頭。 無論在哪里開始,這就是它產生的東西。
嘗試使用[1,1,1,1,1]或“bcdefg”
對函數所做的修改分別反映了每個函數對剩余元素的作用。 賠率丟棄它,evens將它附加到輸出列表的末尾。
如果給出以偶數開頭的列表,則這兩個函數僅能正常工作。 如果給出具有奇數的列表,則它們反向工作。
這是一個函數,它將根據指定的起始編號生成偶數或奇數列表,並以指定的結束編號結束。
eo s e = [s,s+2..e]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.