【问题标题】:Defining my own isPrefixOf without recursion using foldr使用 foldr 定义我自己的 isPrefixOf 而不递归
【发布时间】:2022-02-08 22:05:56
【问题描述】:

我正在进行一项编程任务,我必须仅使用 foldr、map 和缺点(因此没有递归)从 Data.List 定义我自己的 isPrefixOf 版本。我得到的提示是foldr 的返回值本身应该是一个函数。有人可以帮助我了解如何应用该事实吗?我对结构的猜测如下。

startsWith :: String -> String -> Bool
startsWith s1 s2 = (foldr (???) ??? s1) s2

我可以定义自己的辅助函数。出于好奇,这是来自宾夕法尼亚大学 CIS 552 的作业。

  • 您是否有权使用 foldl 解决此问题?
  • @S4eed3sm,左折叠在这个问题上有一个特殊的性能问题。
  • 是的,你已经开始沿着正确的道路。我敦促您用_c 替换第一个???,用_n 替换第二个并编译您的模块。 GHC 会告诉您需要放入“孔”中的值的类型。
  • 首先,弄清楚当 s1 的长度为零时foldr 应该返回哪个函数。 (这很容易:对于任何参数总是返回True)。用它替换第二组???。然后,找出当 s1 的长度为 n+1 时 foldr 应该返回哪个函数,并且当长度为 n 时,您已经有了一个可以正常工作的函数。
  • @s4eed3sm、foldl 和 foldl\' 用于列表不能短路,因此即使早期存在不匹配,它们也会遍历整个前缀。

标签: haskell functional-programming fold


【解决方案1】:

编辑:事实证明,通过折叠模式而不是字符串,代码变得更简单、更短,类似于

"" `isPrefixOf` undefined

作品。谢谢@dfeuer 和@WillNess。这是更新的程序:

isPrefixOf pattern s = foldr g (const True) pattern s
    where
        g x r (h:t)
          | x == h    = r t
          | otherwise = False

它的工作方式与以下程序几乎相同,因此请参阅该程序的说明。


我设法只使用foldr 来解决它:

isPrefixOf :: String -> String -> Bool
p `isPrefixOf` s = isPrefixOfS p
    where
        isPrefixOfS 
            = foldr 
                (\c acc -> 
                    \str -> case str of
                        x:xs -> if c == x
                                then acc xs
                                else False
                        []   -> True 
                    )
                null
                s

这是解释。

为了创建函数isPrefixOf,我们需要这样:

isPrefixOf pattern s 
  = case pattern of
      [] -> True
      x:xs -> if (null s) then False
              else if (head s) /= x
                   then False
                   else xs `isPrefixOf` (tail s)
              

好吧,让我们简化一下——让我们创建一个名为isPrefixOfS 的函数,它只接受一个模式,它会自动与s 进行比较。我们需要构建这个嵌套函数链:

-- Pseudocode, not actual Haskell

\p -> case p of 
        [] -> True
        x:xs -> if x /= (s !! 0)
                  then False
                  else <apply xs to> \q -> case q of [] -> True
                                                     x:xs -> if x /= (s !! 1) -- Note that we've incremented the index
                                                             then False
                                                             else <apply xs to> \r -> ....

这似乎很容易解释 - 如果需要进一步解释,请在评论中告诉我。
好吧,我们可以看到这个链有一个递归属性,s 的最后一个字符将在嵌套最深的 lambda 中进行比较。所以我们需要从右到左嵌套 lambdas。我们可以为此使用什么? foldr。

isPrefixOfS 
            = foldr -- Folding from right to left across `s`
                (\c acc -> -- `c` is the current character of `s`, acc is the chain of nested functions so far
                    \str -> -- We return a function in the fold that takes a String
                      case str of
                        -- If the string is not null:
                        x:xs -> if c == x   -- If the head of the string is equal to the current character of s,
                                then acc xs -- Then pass the tail of the string to the nested function which will compare it with subsequent characters of s
                                else False -- Otherwise we return false
                
                        -- If the string is null, we have completely matched the prefix and so return True
                        []   -> True  
                    )
                null -- Innermost nested function - we need to make sure that if the prefix reaches here, it is null, i.e. we have entirely matched it
                s

现在我们在p 上使用这个函数isPrefixOfS:

p `isPrefixOf` s = isPrefixOfS p

编辑: 我刚刚找到this post,它使用类似的逻辑来实现zip,就foldr而言,你可能也想看看。

【讨论】:

  • 微缩:c == x &amp;&amp; acc xs
  • 您的答案实际上比它需要的要复杂一些,因此也略有错误。 [] `isPrefixOf` undefined 应该是 True,但你的代码是 undefined。你明白为什么吗?
  • @dfeuer 对于您的第一条评论 - 这实际上是我的原始代码,但我让它更加冗长以公开它背后的逻辑。对于第二条评论 - 我没有注意到,感谢您指出。 isPrefixOf 定义中的简单模式匹配将修复它。什么程序太复杂了?
  • 确实 and $ zipWith (==) [] undefined 返回 True 就像你的第二个定义一样,所以第一个不是它的真正翻译。 2d 版本的直接翻译折叠了第一个参数,就像它的原型一样:isPrefixOf pattern s = foldr g (const True) pattern s where g x r (h:t) | x==h = r t | otherwise = False。切换首先折叠的东西确实使事情复杂化。
  • 对于某人的家庭作业问题来说,这有点太详细了。你想指导他们,而不是为他们做功课。
猜你喜欢
  • 1970-01-01
  • 2019-12-06
  • 2019-06-28
  • 2019-10-04
  • 1970-01-01
  • 2011-02-11
  • 1970-01-01
  • 2018-02-11
  • 2011-06-23
相关资源
最近更新 更多