简体   繁体   English

谓词和列表搜索haskell

[英]predicate and a list search haskell

I am learning Haskell at the moment and have come to a bit of a standstill.我现在正在学习 Haskell 并且有点停滞不前。 I'm trying to write a function that takes a predicate p and a list xs and returns the list of those elements of xs which immediately follow an element which passes the predicate p .我想编写一个函数,需要一个谓词p和一个列表xs和回报的那些元素的列表xs其紧跟其通过谓词的元素p Here is what I have :这是我所拥有的:

afterFilter :: (a -> Bool) -> [a] -> [a]

afterFilter x (y:ys) =

    if x y

        then (map head [ys])

    else

        afterFilter x (tail ys) 

test input : afterFilter (<0) [-4,7,-4,-8,3,-3,-6,0,-9,-1]测试输入: afterFilter (<0) [-4,7,-4,-8,3,-3,-6,0,-9,-1]

output : [7]输出: [7]

The trick is to pull two elements out of the input list by pattern-matching two cons cells.诀窍是通过模式匹配两个 cons 单元从输入列表中拉出两个元素。 If the first element passes the predicate, we stick the second on the output.如果第一个元素通过谓词,我们将第二个元素放在输出上。 But don't forget to stick the second element back on the input list when you make the recursive call.但是不要忘记在进行递归调用时将第二个元素贴回到输入列表中。

afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter f [] = []   -- input list is empty
afterFilter f [x] = []  -- input list has only one element - no "next element" to return
afterFilter f (x:y:xs) =
    let ys = afterFilter f (y:xs)
    in (if f x then y:ys else rest)

However, a higher-level - and much more Haskellish - way to approach the problem would be to break it down into a pipeline of operations.然而,解决这个问题的一个更高层次——更Haskellish——的方法是将它分解成一个操作管道。

  1. Pair up each item in the list with the element that follows it using zip , so we have a list of (element, next) pairs.使用zip将列表中的每个项目与它zip的元素配对,所以我们有一个(element, next)对的列表。
  2. Use filter to drop the pairs for which element does not pass the predicate.使用filter删除element未通过谓词的对。
  3. Use map to extract the next part of each surviving pair.使用map提取每个幸存对的next一部分。

So the code looks like this:所以代码看起来像这样:

pairWithSuccessors :: [a] -> [(a, a)]
pairWithSuccessors xs = zip xs (tail xs)

afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter p xs =
    let withSuccessors = pairWithSuccessors xs (tail xs)
        filtered = filter (\(element, next) -> p element) withSuccessors
        filteredSuccessors = map (\(element, next) -> next) filtered
    in filteredSuccessors

Or, written in point-free style :或者,以无点风格编写:

afterFilter p = map snd . filter (p . fst) . pairWithSuccessors

Functions built with the composition operator .使用组合运算符构建的函数. are read right-to-left: first pairWithSuccessors , then filter (p . fst) , then map snd over the result.从右到左读取:首先pairWithSuccessors ,然后filter (p . fst) pairWithSuccessors filter (p . fst) ,然后map snd到结果上。

GHC is good at working with lists: when compiled with optimisations, both approaches should produce roughly the same machine code - that is, there's no performance cost to the high-level solution GHC 擅长处理列表:当使用优化编译时,两种方法应该产生大致相同的机器代码 - 也就是说,高级解决方案没有性能成本

Following what you did, there are some strange things with your code :按照您的操作,您的代码有一些奇怪的地方:

The map head [ys] is very odd, and causes your function to stop : At the first element matching the predicate, your function returns a list containing its immediate successor and stops there. map head [ys]非常奇怪,并导致您的函数停止:在匹配谓词的第一个元素处,您的函数返回一个包含其直接后继的列表并在那里停止。 You still need to process the rest of the list.您仍然需要处理列表的其余部分。

Also, following your definition of the problem, each item which is a successor of an item passing the predicate should be on the resulting array.此外,按照您对问题的定义,作为传递谓词的项的后继项的每个项都应位于结果数组中。 I may be wrong, but what I understood is that afterFilter (<0) [-1, -1, 1] should return [-1, 1] .我可能是错的,但我的理解是afterFilter (<0) [-1, -1, 1]应该返回[-1, 1]

However, you're discarding one element you didn't check for by calling tail ys : You checked for y , but not for head ys .但是,您通过调用tail ys丢弃了一个您没有检查过的元素:您检查了y ,但没有检查head ys

Finally, by adding the edge cases, here is what you get :最后,通过添加边缘情况,您将得到:

afterFilter :: (a -> Bool) -> [a] -> [a]

afterFilter _ [] = []
afterFilter _ [_] = []
afterFilter x (y:ys@(z:zs)) =
    if x y
        then z : afterFilter x ys
    else
        afterFilter x ys

Try:尝试:

afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter p [] = []
afterFilter p [_] = []
afterFilter p (x1:x2:xs) 
  | p x1 = x2:rest
  | otherwise = rest
  where rest = afterFilter p (x2:xs)

Or或者

afterFilter' :: (a -> Bool) -> [a] -> [a]
afterFilter' p xs = map snd $ filter (\(x, _) -> p x) $ zip xs (tail xs)

Or或者

afterFilter'' :: (a -> Bool) -> [a] -> [a]
afterFilter'' p xs = [y | (x, y) <- zip xs (tail xs), p x]

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

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