【问题标题】:making a takeUntil function in haskell在haskell中创建一个takeUntil函数
【发布时间】:2014-10-23 13:41:57
【问题描述】:

我想做一个函数,当给定一个字符串时,例如“ab”和“cdabd”,当它将用于这两个字符串时,它将输出“cd”

到现在为止

takeUntil :: String -> String -> String
takeUntil [] [] = []
takeUntil xs [] = []
takeUntil [] ys = []
takeUntil xs ys = if contains xs ys then -- ???? I get stuck here. 

包含函数是我之前定义的函数(整个函数应该不区分大小写) 包含函数:

contains :: String -> String -> Bool
contains _ [] = True
contains [] _ = False
contains xs ys = isPrefixOf (map toLower ys) (map toLower xs) || contains (tail(map toLower xs)      (map toLower ys)

【问题讨论】:

  • 你的定义有点错误,你的意思是最后4行的takeUntil吗?
  • 是的,我做到了。应该是 takeUntil
  • 另外,如果您可以发布contains 的代码,或者至少发布类型签名和更具体的功能描述,那将非常有帮助。
  • 我猜我可能需要一个辅助函数来定义如果 xs 在 ys 中会发生什么。辅助函数应该在两个字符串匹配之前切断所有内容,即 "ab" "cdab" = "cd"
  • 有很多方法可以解决这个问题,但我认为使用contains 会让事情变得困难。一方面,如果我传入takeUntil "ab" "abcdab" 会怎样?使用contains,这将返回"" 而不是"cdab"。正如@JosEdu 所做的那样,直接使用isPrefixOf 会是更好的选择。

标签: haskell take


【解决方案1】:

有很多方法可以做到这一点,但继续你的路径,尝试以下方法:

import Data.List

takeUntil :: String -> String -> String
takeUntil [] [] = []                           --don't need this
takeUntil xs [] = [] 
takeUntil [] ys = [] 
takeUntil xs (y:ys) = if   isPrefixOf xs (y:ys)
                      then []
                      else y:(takeUntil xs (tail (y:ys)))

一些输出:

takeUntil "ab" "cdabd"
"cd"

takeUntil "b" "cdabd"
"cda"

takeUntil "d" "cdabd"
"c"

takeUntil "c" "cdabd"
""

takeUntil "xxx" "cdabd"
"cdabd"

编辑:

OP 希望函数不区分大小写。

好吧,您可以通过多种方式再次做到这一点。例如,您可以编写一个lowerCase 函数,例如(我认为您已经在Data.Text 中拥有它):

import qualified Data.Char as Char

lowerCase :: String -> String
lowerCase [] = []
lowerCase (x:xs) = (Char.toLower x):(lowerCase xs)

然后像这样使用它(可能很丑而且不太实用):

takeUntil (lowerCase "cd") (lowerCase "abcDe")
"ab"

这就是你所期望的结果。

另外,您可以在takeUntil 中使用lowerCase 函数:

-- ...
takeUntil xs (y:ys) = if  isPrefixOf (lowerCase xs) (lowerCase (y:ys))
-- ...

所以,你可以这样做:

takeUntil "cd" "abcDe"
"ab"

无论如何,我认为最好的选择是@bheklilr 建议的那个。制作你自己的isPrefixOfCaseless 函数。

我希望这会有所帮助。

【讨论】:

  • 谢谢你,但问题是我需要让它不区分大小写,这样如果“cd”“abcDe”它仍然会给我“ab”
  • 尝试在 Data.Charxsy 上使用 toLower
  • takeUntil 的第一种情况被第二种情况包含,因此可以将其删除。此外,tail (y:ys) 可以简化为 ys
  • @Nicholas 如果您想在不区分大小写的情况下执行此检查,我建议创建一个isPrefixOfCaseless(比isPrefixOfCaseInsensitive 更短的名称)函数,在其参数上本地映射toLower。你最终会重复一些操作,但在这种情况下,最好让它工作而不是让它尽可能快地运行。
  • 对,caselessPrefixOf pre list = isPrefixOf (map toLower pre) (map toLower list) 会很有价值。您也可以使用 Data.List 中的其他功能; takeUntil xs ys = maybe ys fst $ find (isPrefixOf xs . snd) $ zip (inits ys) (tails ys) 将其简化为具有复杂逻辑的单行代码,您只需将 caselessPrefixOf 换成它即可。
【解决方案2】:

在定义takeUntil的众多方法中,考虑使用Data.Text函数,如下所示,

takeUntil :: String -> String -> String
takeUntil sep txt =  unpack $ fst $ breakOn (pack sep) (toCaseFold $ pack txt)

注意packString 转换为Text,而uncpak 则相反; toCaseFold 用于不区分大小写的操作; breakOn 提供一对,其中第一个元素包含文本,直到第一个(可能的)匹配。

更新

这种方法涵盖了已经建议的测试,但它不保留原始的String,例如这里,

takeUntil "e" "abcDe"
"abcd"

解决此问题的方法包括例如在断点处按索引拆分。

【讨论】:

    猜你喜欢
    • 2014-04-23
    • 2021-11-11
    • 2018-03-22
    • 2019-02-06
    • 2010-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-18
    相关资源
    最近更新 更多