5

我的作业任务是如果第一个元组的第二个元素与第二个元组的第一个元素相同,则将两个元组分组到一个列表中。然后,如果第一个元组是(a, b),第二个是(b, c),则(a, c)必须将元组添加到结果列表中。

我写了第一个函数,它使用一个元组获取元素,第二个列表包含许多元组,并将每个元素进行比较。

这个工作正常:

c1 = ("a","x")
d1 = [ ("x","b"), ("z","c"), ("x","b"), ("z","c")
     , ("x","b"), ("z","c"), ("x","b"), ("z","c") ]

getByOne c1 a1 = filter (/=[])
  [ if (fst  (last(take n a1))) == (snd c1)
    then [((fst c1),  (snd  (last(take n a1))))]
    else  [] | n <- [1..(length a1) ] ]

输出:

[ [("a","b")], [("a","b")], [("a","b")], [("a","b")] ]

但问题是我不能将 if then 和 else 语句只放入简单的元组,所以我创建了一个新列表。在这个“解决方法”的最后,我得到了列表中的列表等等。此外,如果输出列表更大,列表中的列表也会更多。

有没有办法只传递元组或空元组,或者我应该以某种方式对这些列表进行分组?

4

2 回答 2

9

您可以使用扁平化结果

concat :: [[a]] -> [a]

那么你甚至不需要filter (/=[])- 顺便说一句,条件(/= [])写得更惯用not . null,因为null测试不会Eq对其参数施加约束(你在这里已经有一个约束,所以这只是一个习语问题)。

此外,last (take n a1)只是if的n第 - 个元素。由于您施加了该限制,因此可以更简洁地表示为a11 <= n <= length a1a1 !! (n-1)

然后你在结构上

getByOne c1 a1 = concat $ [something c1 (a1 !! i) | i <- [0 .. length a1 - 1]]

(我已将索引转换为 index by i),它更清晰、更有效地表示为

getByOne c1 a1 = concat $ map (something c1) a1

如果您更喜欢列表推导map,也可以将其写为

getByOne c1 a1 = concat [something c1 x | x <- a1]

在您的情况下,使用 alist-comprehension 生成器中的模式匹配能力,给了我们

getByOne (f,s) a1 = concat [ if a == s then [(f,b)] else [] | (a,b) <- a1]

它更短且更具可读性。而不是使用if condition then [element] else []and concat,更好的是在列表理解中使用条件,

getByOne (f,s) list = [(f,b) | (a,b) <- list, a == s]

这是简短而清晰的。

于 2012-11-04T10:16:44.870 回答
3
  1. [1..(length a1)]可以写[1 .. length a1]
  2. [ if (fst  (last(take n a1))) == (snd c1)
      then [((fst c1),  (snd  (last(take n a1))))]
      else  [] | n <- [1..(length a1) ] ]
    

    可以写

    [ if fst lastElement == snd c1
      then [(fst c1, snd lastElement)]
      else [] | n <- [1 .. length a1 ]
              , let lastElement = last (take n a1) ]
    
  3. 然后,不要使用列表索引来遍历它,而是直接使用列表:

    [ if x == snd c1
      then [(fst c1, y)]
      else [] | (x, y) <- a1 ]
    
  4. 然后,不要使用列表来表示解决方案的存在与否,而是使用Maybe

    import Data.Maybe
    
    c1 = ("a","x")
    d1 = [ ("x","b"), ("z","c"), ("x","b"), ("z","c")
         , ("x","b"), ("z","c"), ("x","b"), ("z","c") ]
    
    getByOne c1 a1 = catMaybes
            [ if x == snd c1
              then Just (fst c1, y)
              else Nothing | (x, y) <- a1 ]
    
  5. 更好的是,使用警卫并摆脱 if then else:

    getByOne (a, b) a1 = [ (a, d) | (c, d) <- a1
                                  , b == c ]
    

或者,如果你想使用过滤器,你首先过滤匹配的元组列表,然后用 map 构建相应的结果:

getByOne (a, b) a1 = map (\(_, c) -> (a, c))
                     . filter (\(c, _) -> b == c)
                     $ a1

这简化为

getByOne (a, b) = map (\(_, c) -> (a, c))
                     . filter (\(c, _) -> b == c)
于 2012-11-04T10:24:40.480 回答