Haskell中的字符串组合

问题描述

我正在用Haskell编写一种算法,以简化上下文无关的语法,而且我一直在努力消除null产生式,更具体地说,是用其他产生物中的nullable非终结符的“替代”。

给出一个字符串,比方说“ ASA”,我想返回一个通过删除每次出现的字符“ A”一,二,...而构建的字符串列表。 明确地说,鉴于“ ASA”,我想返回以下代码["SA","AS","S"]

在Python中,我做起来很容易,但是在Haskell中,我不知道如何遍历字符串并按我的意愿操纵它。可能是因为我仍然不习惯使用函数式编程。

解决方法

基于库的方法:

给定的输入字符可能会或可能不会出现在任何输出部分字符串中。因此,让Haskell Maybe type transformer参与进来似乎很自然。它类似于C ++中的std::optional

我们可以拥有一个expand函数,该函数将与每个输入字符相关联的对应可能性的列表:

$ ghci
 λ> 
 λ> st = "ASA"
 λ> 
 λ> expand ch = if (ch == 'A') then [ Just ch,Nothing ] else [ Just ch ]
 λ> 
 λ> map expand st
 [[Just 'A',Nothing],[Just 'S'],[Just 'A',Nothing]]
 λ> 

基本上,我们需要的是上述列出的可能性的笛卡尔积。列表笛卡尔积可以通过使用高度多态的sequence库函数来获得:

 λ> 
 λ> sequence (map expand st)
[[Just 'A',Just 'S',Just 'A'],[Nothing,Nothing]]
 λ> 

接下来,我们需要将[Just 'A',Nothing]更改为['A','S'],在Haskell中它与“ AS”完全相同。所需的功能将具有其类型签名:

func :: [Maybe α] -> [α]

如果我们将此候选类型签名提交到Hoogle,我们很容易获得库函数catMaybes

 λ> 
 λ> import qualified Data.Maybe as Mb
 λ> 
 λ> Mb.catMaybes [Just 'A',Nothing]
 "AS"
 λ> 
 λ> map Mb.catMaybes (sequence (map expand st))
 ["ASA","AS","SA","S"]
 λ> 

,我们只需要从最后一个列表中删除完整的字符串“ ASA”即可。

当然,没有必要将其限制为Char数据类型。具有适当的相等性测试的任何类型都可以。特权字符“ A”应设为可变参数。总体而言,这为我们提供了以下代码:

import qualified Data.Maybe as Mb

multiSuppressor :: Eq α => α -> [α] -> [[α]]
multiSuppressor e xs =
    let  expand e1 = if (e1 == e) then [ Just e1,Nothing ] else [ Just e1 ]
         maybes  = sequence  (map expand xs)
         res1    = map  Mb.catMaybes  maybes
    in
         -- final massaging as the whole list is normally unwanted:
         if (null xs)  then  [[]]  else  filter (/= xs) res1

关于效率的说明:

函数sequence是多态的。列表中的笛卡尔积不是生命中唯一的角色。不幸的是,这碰巧会带来不幸的副作用,即如果您超出玩具大小的示例,其内存消耗可能会变得非常大。

如果这成为问题,则可以改用以下基于an idea by K. A. Buhr的替换代码:

cartesianProduct :: [[α]] -> [[α]]
cartesianProduct xss =
    map reverse (helper (reverse xss))
        where
            helper [] = [[]]
            helper (ys:zss) =  [y:zs | zs <- helper zss,y <- ys]