簡體   English   中英

獲得列表中元素的偶數和奇數位置 - Haskell Mutual Recursion

[英]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的列表。

繼續到偶數位置的元素, ac將是明顯的答案,而bd將處於奇數位置。 讓我們來看看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]

或者使用顯式mapfilter

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.

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