简体   繁体   English

左填充 Haskell 列表

[英]Left pad a Haskell list

If I wanted to right-pad a Haskell list of integers, I could do something like the following:如果我想右填充 Haskell 整数列表,我可以执行以下操作:

rpad m xs = take m $ xs ++ repeat 0

There would be no need to get the length of the list.无需获取列表的长度。 I think it would be quite performant.我认为它会非常高效。

Is there a similar way I could definte lpad , padding the list on the left, without incurring the cost of counting the length, etc. ?有没有类似的方法可以定义lpad ,在左侧填充列表,而不会产生计算长度等的成本?

So the first thing worth saying is, don't worry too much about the nitty-gritty of performance .所以首先值得说的是,不要太担心性能的本质 This code is unlikely to be sitting in the proverbial 20% of your code which takes up 80% of the running time.这段代码不太可能占据众所周知的 20% 的代码,占用 80% 的运行时间。

With that said, where does performance really matter, here?话虽如此,性能真正重要的地方在哪里? It matters if m is small while length xs is huge or infinite.如果m很小而length xs很大或无限,这很重要。 I mean, it would also be nice to get good performance if m is large (as you have with rpad , where if you process only the first k items of the list you only do k work for some k << m ), but of course the very problem description requires that you potentially do m work to see the top result.我的意思是,如果m很大(就像使用rpad ,如果你只处理列表的前k项目,你只能为某些k << mk工作),那么获得良好的性能也会很好,但是当然,很说明问题需要你做的潜在m的工作,看上面的结果。 (If you're provided an infinite list you need to peek at m items to even know whether to return 0 for the first element.) (如果您提供了一个无限列表,您需要查看m项目甚至知道是否为第一个元素返回0

In that case, you really want to zero-pad take m xs instead of xs .在这种情况下,您真的想要零填充take m xs而不是xs That's the entire trick:这就是整个技巧:

lpad m xs = replicate (m - length ys) 0 ++ ys
    where ys = take m xs

Right padding only needs to examine the first constructor in the input list ( : or [] ) in order to produce the first element of the output list.右填充只需检查输入列表( :[] )中的第一个构造函数即可生成输出列表的第一个元素。 It's a streaming operation (could be done with foldr ).这是一个流操作(可以用foldr完成)。

Left padding needs to examine the whole input list in order to produce the first element of the output list.左填充需要检查整个输入列表以生成输出列表的第一个元素。 That is, whether the first element is 0 or not depends on the tail of the list (assuming it does not start with 0 ).也就是说,第一个元素是否为0取决于列表的尾部(假设它不是以0开头)。 This can not be done in a streaming way.这不能以流式传输方式完成。 O(min(m,length)) is the best you can get, for the first element only. O(min(m,length)) 是您能得到的最好结果,仅适用于第一个元素。

Also, be careful since your padding function discards elements after the m -th, if your input list is longer than that.另外,请注意,如果您的输入列表长于第m个,您的填充函数会丢弃第m个之后的元素。 This might be unwanted -- sometimes padding is defined so that it can only add elements, and never remove.这可能是不需要的——有时定义了填充,以便它只能添加元素,而永远不会删除。

Here is an (untested but compiling) set of padding/trimming functions ( Unlicence d):这是一组(未经测试但正在编译)填充/修剪函数( Unlicence d):

padL :: a -> Int -> [a] -> [a]
padL p s l
    | length l >= s = l
    | otherwise     = replicate (s - length l) p ++ l
{-# INLINABLE padL #-}

padR :: a -> Int -> [a] -> [a]
padR p s l = take s $ l ++ repeat p
{-# INLINABLE padR #-}

trimL :: Int -> [a] -> [a]
trimL s l
    | length l <= s = l
    | otherwise     = drop (length l - s) l
{-# INLINABLE trimL #-}

trimR :: Int -> [a] -> [a]
trimR = take
{-# INLINE trimR #-}

resizeL :: a -> Int -> [a] -> [a]
resizeL p s l
    | length l == s = l
    | length l < s  = padL p s l
    | otherwise     = trimL s l
{-# INLINABLE resizeL #-}

resizeR :: a -> Int -> [a] -> [a]
resizeR p s l
    | length l == s = l
    | length l < s  = padR p s l
    | otherwise     = trimR s l
{-# INLINABLE resizeR #-}

Here is an Unlicence d, (untested but compiling), probably-optimal-in-terms-of-speed set of padding/trimming functions: 这是一个Unlicence d,(未经测试但编译),可能是速度最佳的填充/修剪功能集:

padL :: a -> Int -> [a] -> [a]
padL p s l
    | length l >= s = l
    | otherwise     = replicate (s - length l) p ++ l
{-# INLINABLE padL #-}

padR :: a -> Int -> [a] -> [a]
padR p s l = take s $ l ++ repeat p
{-# INLINABLE padR #-}

trimL :: Int -> [a] -> [a]
trimL s l
    | length l <= s = l
    | otherwise     = drop (length l - s) l
{-# INLINABLE trimL #-}

trimR :: Int -> [a] -> [a]
trimR = take
{-# INLINE trimR #-}

resizeL :: a -> Int -> [a] -> [a]
resizeL p s l
    | length l == s = l
    | length l < s  = replicate (s - length l) p ++ l
    | otherwise     = drop (length l - s) l
{-# INLINABLE resizeL #-}

resizeR :: a -> Int -> [a] -> [a]
resizeR p s l
    | length l == s = l
    | length l < s  = take s $ l ++ repeat p
    | otherwise     = take s l
{-# INLINABLE resizeR #-}

@Solomon Ucko @所罗门乌科

Why call length twice in padL (ditto for others)?为什么在padL调用length两次(其他人同上)? How about怎么样

padL :: a -> Int -> [a] -> [a]
padL p s l
    | length' >= s = l
    | otherwise    = replicate (s - length') p ++ l
      where length' = length l

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM