【问题标题】:Haskell - if else return clean tupleHaskell - 如果否则返回干净的元组
【发布时间】:2012-10-24 10:18:12
【问题描述】:

如果第一个元组的第二个元素与第二个元组的第一个元素相同,我的作业任务是将两个元组分组到一个列表中。那么,如果第一个元组是(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 语句只放入简单的元组,所以我创建了一个新列表。在这个“解决方法”的最后,我得到了列表中的列表等等。此外,如果输出列表更大,列表中的列表也会更多。

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

【问题讨论】:

  • 但这不是您想要的输出,对吧?你想要什么输出做,我不清楚。
  • 好吧,我会尽量自己解决这个任务,只要它是我家庭作业的一部分。再次感谢您的帮助。

标签: list haskell tuples


【解决方案1】:
  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)

【讨论】:

  • 在concat 下使用[] 条目来表达消除(过滤)是非常惯用的,并且if-the-else 可以进一步替换为另一种理解,在您的#3:[[(fst c1, y) | snd c1==x] | (x, y) &lt;- a1 ],直接简化为您的#5。 :)
【解决方案2】:

您可以使用扁平化结果

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

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

此外,last (take n a1) 只是a1 的n-th 元素,如果1 &lt;= n &lt;= length a1。既然你有这个限制,那可以更简洁地表达为a1 !! (n-1)

那么你有结构

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

(我已将索引转换为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 [] 和concat,不如在列表推导中使用条件,

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

简洁明了。

【讨论】:

  • +1 看到这样的东西让我想起了 Haskell 代码的可读性,结果函数比口头描述更清晰、更短。
  • concat :: [[a]] -> [a] 在这种情况下非常有用。
猜你喜欢
  • 2018-04-07
  • 2017-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-10
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
相关资源
最近更新 更多