当长度不到3次以下无限列表时,我该如何总和列表列表?

发布于 2025-02-11 04:49:27 字数 423 浏览 1 评论 0原文

我的代码正在起作用,但不适合无尽的列表。我该如何使其工作?

sumsOf :: Num a => [[a]] -> [a]
sumsOf a = map sum [ x | x <- a , length x < 3]

示例:

sumsOf [[],[1,2,4],[],[6]] == [0,0,6]
sumsOf [[1],[8],[6],[],[9,9]] == [1,8,6,0,18]
sumsOf [[1,2,9,10],[7,8,9],[6,9,4,2,0],[9,9,9]] == []
sumsOf [[1..],[7..],[6..],[9..],[10..],[100..]] == []
sumsOf [[1,2], [1..], [], [4]] == [3,0,4]

My code is working but not for endless lists. How can I make it work ?

sumsOf :: Num a => [[a]] -> [a]
sumsOf a = map sum [ x | x <- a , length x < 3]

Examples :

sumsOf [[],[1,2,4],[],[6]] == [0,0,6]
sumsOf [[1],[8],[6],[],[9,9]] == [1,8,6,0,18]
sumsOf [[1,2,9,10],[7,8,9],[6,9,4,2,0],[9,9,9]] == []
sumsOf [[1..],[7..],[6..],[9..],[10..],[100..]] == []
sumsOf [[1,2], [1..], [], [4]] == [3,0,4]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

捂风挽笑 2025-02-18 04:49:27

长度是您应该按照经验的列表函数之一从不使用使用,除非您确切地知道为什么还可以。 (另一个是head!!。)这不仅是因为它与无限列表不起作用,还因为它通常完全不必要的 o n )在整个列表上进行遍历。即使您以后需要遍历它,这两次也可能会带来很大的性能差异。

在这种情况下,请考虑您真正需要的内容:sum避免进入漫长或无限列表,而是在两个元素后放弃。实际上,这可以以一种愚蠢的疲惫方式实现:

sumIfLenLt3 :: Num a => [a] -> Maybe a
sumIfLenLt3 [] = Just 0
sumIfLenLt3 [x] = Just x
sumIfLenLt3 [x,y] = Just $ x+y
sumIfLenLt3 _ = Nothing

为了使其对任何最大长度进行一般性,您可以使用递归:

sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe _ [] = Just 0
sumIfLenLe 0 _ = Nothing
sumIfLenLe n (h:t) = h + sumIfLenLe (n-1) t

...但是,这不是一个很好的实现,原因有两个:它会以负面的论点炸毁,并且不是尾部递归。一个更好的,尽管有点麻烦,但版本会

sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe n l
  | n<0        = Nothing
  | otherwise  = go n l 0
 where go n [] acc  = Just acc
       go 0 _ _ = Nothing
       go n (h:t) acc = go (n-1) t (h+acc)

更优雅,是使用标准功能来获取最大可用元素的计数,并查看是否剩下任何东西:

sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe n l
  | null remains  = Just $ sum relevant
  | otherwise     = Nothing
 where (relevant, remains) = splitAt n l

...或,某些等效的形式,

sumIfLenLe n l = case splitAt n l of
   (relevant, []) -> Just $ sum relevant
   _              -> Nothing

或者

sumIfLenLe n l
 | (relevant, []) <- splitAt n l
              = Just $ sum relevant
 | otherwise  = Nothing

您可以只是可以在整个给定的列表中绘制该图,并收集成功的所有结果。

import Data.Maybe (catMaybes)

sumsOf :: Num a => [[a]] -> [a]
sumsOf = catMaybes . map (sumIfLe 2)

length is one of the list functions that you should as a rule of thumb never use except when you know exactly why it's ok. (The other being head and !!.) It's not just because it doesn't work with infinite lists, but also because it does a usually completely unnecessary O(n) traversal over the entire list. Even if you need to traverse it anyway later, doing it twice can make a big performance difference.

In this case think about what you really need: a sum that avoids going into an long or infinite list but instead gives up after two elements. That could actually be implemented in a stupid exhaustion way:

sumIfLenLt3 :: Num a => [a] -> Maybe a
sumIfLenLt3 [] = Just 0
sumIfLenLt3 [x] = Just x
sumIfLenLt3 [x,y] = Just $ x+y
sumIfLenLt3 _ = Nothing

To make it general for any max-length, you could use recursion:

sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe _ [] = Just 0
sumIfLenLe 0 _ = Nothing
sumIfLenLe n (h:t) = h + sumIfLenLe (n-1) t

...but that's not such a good implementation, for two reasons: it blows up with negative arguments, and it isn't tail recursive. A better, albeit a bit cumbersome, version would be

sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe n l
  | n<0        = Nothing
  | otherwise  = go n l 0
 where go n [] acc  = Just acc
       go 0 _ _ = Nothing
       go n (h:t) acc = go (n-1) t (h+acc)

A bit more elegant is to use a standard function to take the max usable count of elements, and see if anything remains:

sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe n l
  | null remains  = Just $ sum relevant
  | otherwise     = Nothing
 where (relevant, remains) = splitAt n l

...or, some equivalent forms,

sumIfLenLe n l = case splitAt n l of
   (relevant, []) -> Just $ sum relevant
   _              -> Nothing

or

sumIfLenLe n l
 | (relevant, []) <- splitAt n l
              = Just $ sum relevant
 | otherwise  = Nothing

Then you can just map that over the entire given list and gather all the result that succeeded.

import Data.Maybe (catMaybes)

sumsOf :: Num a => [[a]] -> [a]
sumsOf = catMaybes . map (sumIfLe 2)
你的往事 2025-02-18 04:49:27

简单液体解决方案:

您只关心长度是否小于3,因此,如果您将输入列表截断为3个元素,则结果不会更改:

$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/  :? for help
 λ> 
 λ> sumsOf a = map sum [ x | x <- a , length (take 3 x) < 3]
 λ> 
 λ> sumsOf [[],[1,2,4],[],[6]]
 [0,0,6]
 λ> sumsOf [[1],[8],[6],[],[9,9]]
 [1,8,6,0,18]
 λ> sumsOf [[1,2,9,10],[7,8,9],[6,9,4,2,0],[9,9,9]]
 []
 λ> sumsOf [[1..],[7..],[6..],[9..],[10..],[100..]]
 []
 λ> sumsOf [[1,2], [1..], [], [4]]
 [3,0,4]
 λ> 

因此,这很容易通过无限列表解决问题。

以稍微惯用的方式:

sumsOf :: Num a => [[a]] -> [a]
sumsOf xss = map sum [ xs | xs <- xss , length (take 3 xs) < 3]

唯一的缺点是库函数tot必须复制列表节点。

N.1.8e9-在评论中提到的一个想法,通过编写一些boundedlength函数,可以使某些内容更有效,以最多返回k < /code>,例如这样:

boundedLength :: Int -> [a] -> Int
boundedLength k xs = go 0 xs
  where
    go depth   []    =  depth
    go depth (x:xs)  =  if (depth < k)  then  go (depth + 1) xs
                                        else  k

在这种情况下,我们的sumsof函数最终以:

sumsOf :: Num a => [[a]] -> [a]
sumsOf xss = map sum [ xs | xs <- xss , boundedLength 3 xs < 3]

Easy-lazy solution:

You are only concerned about whether the length is less than 3, so the result is not changed if you truncate your input list at 3 elements:

$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/  :? for help
 λ> 
 λ> sumsOf a = map sum [ x | x <- a , length (take 3 x) < 3]
 λ> 
 λ> sumsOf [[],[1,2,4],[],[6]]
 [0,0,6]
 λ> sumsOf [[1],[8],[6],[],[9,9]]
 [1,8,6,0,18]
 λ> sumsOf [[1,2,9,10],[7,8,9],[6,9,4,2,0],[9,9,9]]
 []
 λ> sumsOf [[1..],[7..],[6..],[9..],[10..],[100..]]
 []
 λ> sumsOf [[1,2], [1..], [], [4]]
 [3,0,4]
 λ> 

So this readily solves the problem with unlimited lists.

In slightly more idiomatic fashion:

sumsOf :: Num a => [[a]] -> [a]
sumsOf xss = map sum [ xs | xs <- xss , length (take 3 xs) < 3]

The sole drawback is that library function take has to duplicate the list nodes.

An idea mentioned in a comment by n.1.8e9-where's-my-sharem: it is possible to have something a bit more efficient, by writing some boundedLength function that returns at most k, for example like this:

boundedLength :: Int -> [a] -> Int
boundedLength k xs = go 0 xs
  where
    go depth   []    =  depth
    go depth (x:xs)  =  if (depth < k)  then  go (depth + 1) xs
                                        else  k

In that case, our sumsOf function ends up as:

sumsOf :: Num a => [[a]] -> [a]
sumsOf xss = map sum [ xs | xs <- xss , boundedLength 3 xs < 3]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文