【问题标题】:Comparing list length with arrows用箭头比较列表长度
【发布时间】:2011-10-11 18:43:39
【问题描述】:

灵感来自Comparing list length

如果我想在列表列表中找到最长的列表,最简单的方法可能是:

longestList :: [[a]] -> [a]
longestList = maximumBy (comparing length)

一种更有效的方法是预先计算长度:

longest :: [[a]] -> [a]
longest xss = snd $ maximumBy (comparing fst) [(length xs, xs) | xs <- xss]

现在,我想更进一步。对于正常情况,它可能不会更有效,但是您能使用箭头解决这个问题吗?我的想法基本上是,同时遍历所有列表,并继续步进,直到超出除最长列表之外的每个列表的长度。

longest [[1],[1],[1..2^1000],[1],[1]]

在前面的(非常人为的)示例中,您只需通过每个列表执行两个步骤即可确定列表 [1..2^1000] 是最长的,而无需确定所述列表的整个长度。我可以用箭头来完成吗?如果是这样,那怎么办?如果不是,那为什么不呢?如何实施这种方法?

【问题讨论】:

  • 我没有看到任何与箭头的联系。
  • @luqui Using arrows 上的 Haskell Wikibook 的一部分似乎指出箭头对于解析很有用,其方式类似于我对这个问题提出的解决方案(查看每个列表,然后是第二个,等等)Stephen's Arrow Tutorial 给了我同样的感觉:箭头可以用来挖掘这些列表并随时存储信息。
  • 我已经接受了一个答案,但是如果有人可以用箭头提出答案,或者彻底解释为什么箭头不相关,那么我肯定会接受那个答案。

标签: list haskell comparison arrows


【解决方案1】:

好的,在我写这个问题的时候,我突然想到了一个简单的方法来实现这个(没有箭头,嘘!)

longest [] = error "it's ambiguous"
longest [xs] = xs
longest xss = longest . filter (not . null) . map (drop 1) $ xss

除非这有问题...它会丢弃列表的第一部分并且不会恢复它!

> take 3 $ longest [[1],[1],[1..2^1000],[1]]
[2,3,4]

需要更多簿记:P

longest xs = longest' $ map (\x -> (x,x)) xs

longest' []   = error "it's ambiguous"
longest' [xs] = fst xs
longest' xss  = longest . filter (not . null . snd) . map (sndMap (drop 1)) $ xss

sndMap f (x,y) = (x, f y)

现在可以了。

> take 3 $ longest [[1],[1],[1..2^1000],[1]]
[1,2,3]

但没有箭头。 :( 如果它可以用箭头来完成,那么希望这个答案能给你一个开始的地方。

【讨论】:

  • 另外,errror "it's ambiguous" 是处理相同长度列表的一种非常糟糕的方法。嗯。这个问题是专门为你有很多短名单和一个很长的情况而设计的。
【解决方案2】:

再想一想,有一个更简单的解决方案可以提供相同的性能特征。我们可以将maximumBy 与惰性长度比较函数一起使用:

compareLength [] [] = EQ
compareLength _  [] = GT
compareLength [] _  = LT
compareLength (_:xs) (_:ys) = compareLength xs ys

longest = maximumBy compareLength

【讨论】:

  • +1 确实很优雅。但是,如果我没记错的话,它会在每次比较 2 个列表时重新遍历列表以查看哪个更长。
  • @DanBurton:是的,但仅限于两个列表中较短的一个。所以这只是一个问题,即哪种方法具有最高的常数因子,我必须承认我没有测试过。
  • 另外,您可以使用二进制数的代数数据类型来表示列表的长度。这样,您只需在列表长度中获得一个对数因子来比较两个列表的长度,并且仍然至少获得某种程度的惰性。
  • 作为旁注longest 是不必要的严格,例如,我们有longest ((_|_:_|_):_|_) == _|_,尽管该函数可能已经产生了一个列表构造函数。虽然maximumBy 的当前实现采用产生Ordering 的函数,但它应该采用谓词。
【解决方案3】:

这是我能想到的最直接的实现。不过不涉及箭头。

我保留了一个对列表,其中第一个元素是原始列表,第二个元素是剩余的尾部。如果我们只剩下一个列表,我们就完成了。否则,我们尝试取所有剩余列表的尾部,过滤掉那些为空的。如果还有一些,请继续。否则都是一样长的,我们随便挑第一个。

longest []  = error "longest: empty list"
longest xss = go [(xs, xs) | xs <- xss]
  where go [(xs, _)] = xs
        go xss | null xss' = fst . head $ xss
               | otherwise = go xss'
               where xss' = [(xs, ys) | (xs, (_:ys)) <- xss]

【讨论】:

  • 可以将第二行更改为longest xss = go $ map (id &amp;&amp;&amp; id) xss,但我想这不是你想的那种箭头用法:)
猜你喜欢
  • 1970-01-01
  • 2021-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
  • 2017-12-24
  • 2019-02-17
相关资源
最近更新 更多