2012-04-23 62 views
0

我必須定義一個稱爲零的函數,它接受兩個列表的輸入並返回一個布爾值,如果數字0在每個列表中顯示的次數相同,則返回True,否則返回false。在Haskel中比較列表

這是我作業中的最後一個問題,而且我已經設法解決了這個問題,但是我想知道是否有人可以找到減少代碼量的方法,任何想法都會受到讚賞。到目前爲止我的代碼如下:

x :: Int 
x = 0 
instances::[Int]->Int 
instances [] = 0 
instances (y:ys) 
    | x==y = 1+(instances ys) 
    | otherwise = instances ys 



zeros :: [Int] -> [Int] -> Bool 
zeros [] [] = False 
zeros x y 
     | ((instances x) == (instances y)) = True 
     | otherwise = False 
+3

似乎我認爲'零[] []'應該等於'真'嗎?至少它看起來不像'zeros [1] []',你的代碼會認爲'真'。我不知道你甚至不需要處理'零[] []'... – sblom 2012-04-23 23:49:57

+0

不,我不認爲它應該是因爲如果列表是空的,那麼它們將不包含零,因此它將是假 – 2012-04-23 23:51:34

+1

我很難說服自己「無零」和「零零」不是同一件事。例如,'zeros [1] []'會按照你寫的東西(都是零)返回True。而'零'[] []'會返回'假'(儘管兩者都有零)。 – sblom 2012-04-23 23:54:27

回答

3

沒有給太多的東西,因爲這是作業,這裏有一些提示。

你知道列表解析嗎?在這種情況下它們會很有用。例如,你可以用if表達結合他們做這樣的事情:

*Main> let starS s = [if c == 's' then '*' else ' ' | c <- s] 
*Main> starS "schooners" 
"*  *" 

你甚至可以利用它們來做過濾。例如:

*Main> let findFives xs = [x | x <- xs, x == 5] 
*Main> findFives [3,7,5,6,3,4,5,7,5,5] 
[5,5,5,5] 

這些都不是一個完整的答案,但不應該很難看出如何使這些結構適應您的情況。

你還應該考慮一下你是否真的需要一個警衛!例如,下面是一個與您的風格一樣寫的功能:

lensMatch [] [] = True 
lensMatch xs ys 
      | ((length xs) == (length ys)) = True 
      | otherwise = False 

這是一個功能,可以做同樣的事情!

lensMatch' xs ys = length xs == length ys 

你可以看到它們是相同的;測試第一:

*Main> lensMatch [1..4] [1..4] 
True 
*Main> lensMatch [1..4] [1..5] 
False 
*Main> lensMatch [] [1..5] 
False 
*Main> lensMatch [] [] 
True 

和測試第二:

*Main> lensMatch' [1..4] [1..4] 
True 
*Main> lensMatch' [1..4] [1..5] 
False 
*Main> lensMatch' [] [1..5] 
False 
*Main> lensMatch' [] [] 
True 

最後,我上面sblom的評論非常強烈同意; zeros [] []應該是True!考慮以下語句:「對於集合s中的每個項目x,x> 0」。如果set s是空的,那麼聲明是真的!這是真的,因爲根本沒有任何物品。這在我看來就像是一個類似的情況。

1

你有一個通想到這樣做的通過過濾每個列表得到公正的零,然後比較列表的長度,看看他們是平等的嗎?

zeroCompare xs ys = cZeroes xs == cZeroes ys 
    where 
    cZeroes as = length $ filter (== 0) as 
+0

我確實認爲,我真的認爲這就是我的導師將尋找,但我不確定如何實現過濾器並一舉掃除 – 2012-04-23 23:55:31

+0

'as'的意義是什麼? – 2012-04-24 00:08:03

+0

'xs'和'ys'的意義是什麼?沒有。這只是一種陳述輸入參數的方式。我可以寫下**點自由風格** cZeroes = length。過濾器(== 0)',但我認爲使用參數理解它會更容易。 – Abizern 2012-04-24 00:11:36

2

我不敢相信沒有人建議使用foldr呢。不是最短的或最好的定義,但IMO最教育:

instances :: Eq a => a -> [a] -> Int 
instances n = foldr incrementIfEqual 0 
    where incrementIfEqual x subtotal 
       | x == n = subtotal + 1 
       | otherwise = subtotal 

zeros :: Num a => [a] -> [a] -> Bool 
zeros xs ys = instances 0 xs == instances 0 ys 

雖然對於instances一個非常簡短的定義,我想出了基本相同的Abizern:

instances :: Eq a => a -> [a] -> Int 
instances x = length . filter (==x) 
0

代替lengthfilter,你可以採取一個謂語p的結果,將其轉換爲0或1,總結結果:

count p = sum . map (fromEnum.p) 

--or 

import Data.List 
count p = foldl' (\x -> (x+).fromEnum.p) 0 

在你的情況下,p當然是(==0)。使用fromEnumBool轉換爲Int是非常有用的技巧。

0

另一個想法是對付列表兩者同時,這是一個有點長,但很容易理解:

zeros xs ys = cmp xs ys == 0 where 
    cmp (0:xs) ys = cmp xs ys + 1 
    cmp xs (0:ys) = cmp xs ys - 1 
    cmp (_:xs) ys = cmp xs ys 
    cmp xs (_:ys) = cmp xs ys 
    cmp [] []  = 0 
0

我會下來把問題分解成涉及輔助功能較小的問題。

這是我怎麼會打破它:

  1. 主要功能來比較兩個數
  2. 計數助手功能

首先:你需要一個方法來計算在零量一個列表。例如,我想如果搜索的0的整數列表中的號碼下面做處理這個:

count :: [Int] -> Int 
count xs = foldl (\count num -> if num == 0 then (count + 1) else count) 0 xs 

二:你需要一種方法來比較兩個表的計數。實質上,您需要一個函數,它將兩個列表作爲參數,計算每個列表的計數,然後根據結果返回一個布爾值。例如,如果每個列表是一個int列表,上面我的計數例如對應:

equalZeroes :: [Int] -> [Int] -> Bool 
    equalZeroes x y = (count x) == (count y) 

您也可以在equalZeroes內,其中關鍵字下的定義計數功能,像這樣:

equalZeroes :: [Int] -> [Int] -> Bool 
equalZeroes x y = (count x) == (count y) 
    where 
     count :: [Int] -> Int 
     count xs = foldl (\count num -> if num == 0 then (count + 1) else count) 0 xs 

運行時這個代碼,調用函數會得到所需的布爾值返回:

equalZeroes [0,1,4,5,6] [1,4,5,0,0] 
-> False 

equalZeroes [0,1,4,5,6] [1,4,5,0] 
-> True