Haskell-基于嵌套列表中的测试排除列表

Haskell-基于嵌套列表中的测试排除列表,haskell,filter,list-comprehension,Haskell,Filter,List Comprehension,我想根据一般规范创建一系列可能的方程式: test = ["12", "34=", "56=", "78"] 每个字符串(例如“12”)表示该位置的一个可能字符,在本例中为“1”或“2”。) 因此,测试中可能的方程式为“13=7”或“1=68”。 我知道我给出的例子并不平衡,但那是因为我故意给出了一个简化的短字符串。 (我也知道我可以使用“序列”搜索所有可能性,但我想变得更聪明,所以我需要一种不同的方法,如下所述。) 我想要的是试着依次修正每个等式,然后去掉等式中的所有其他等式。所以我想: [

我想根据一般规范创建一系列可能的方程式:

test = ["12", "34=", "56=", "78"]
每个字符串(例如“12”)表示该位置的一个可能字符,在本例中为“1”或“2”。) 因此,测试中可能的方程式为“13=7”或“1=68”。 我知道我给出的例子并不平衡,但那是因为我故意给出了一个简化的短字符串。 (我也知道我可以使用“序列”搜索所有可能性,但我想变得更聪明,所以我需要一种不同的方法,如下所述。)

我想要的是试着依次修正每个等式,然后去掉等式中的所有其他等式。所以我想:

[["12","=","56","78"],["12","34","=","78”]]
我写了这个嵌套列表: (需要:{-#语言并行列表comp})

但我想过滤掉其中任何空列表。i、 在这种情况下,第一个和最后一个

我可以做到:

countOfEmpty :: (Eq a) => [[a]] -> Int 
countOfEmpty = length . filter (== [])

fixEqualsFiltered :: [String] -> [[String]]
fixEqualsFiltered re = filter (\x -> countOfEmpty x == 0) (fixEquals re)
因此,“fixEqualsFiltered测试”给出:

这正是我想要的,但看起来并不优雅。 我忍不住想还有别的方法可以过滤掉这些。 毕竟,只要在if语句中使用了“equals”且为空,我们就要删除这些equals,因此构建列表(例如,[,“34”,“56”,“78”],然后丢弃它)似乎是一种浪费

感谢您的任何想法。

让我们

xs = [["","34","56","78"],["12","=","56","78"],["12","34","=","78"],["12","34","56",""]]

将给予

[["12","=","56","78"],["12","34","=","78"]]
如果你想了解列表,那就去做吧

[x | x <- xs, and [not $ null y | y <- x]]

[x | x我不知道这是否比您的代码更清晰,但使用递归可能会更清晰、更高效:

fixEquals = init . f
f :: [String] -> [[String]]
f [] = [[]]
f (x:xs) | '=' `elem` x = ("=":removeEq xs) : map (removeEq [x] ++) (f xs)
         | otherwise    = map (x:) (f xs)

removeEq :: [String] -> [String]
removeEq = map (filter (/= '='))
它的工作方式是,如果当前字符串中有一个
'='
,那么它将返回拆分为两个,如果不是递归调用的话。需要
init
,因为在返回的最后一个元素中,任何字符串中都没有相等的元素


最后,我相信您可能会找到一个更好的数据结构来完成您需要完成的任务,而不是使用字符串列表。我想我可能会这样做。首先,我写了这么多次的一个初步报告,现在它几乎已经烧到我的手指了:

zippers :: [a] -> [([a], a, [a])]
zippers = go [] where
    go _ [] = []
    go b (h:e) = (b,h,e):go (h:b) e
可能在ghci中运行一两次会比我所能做的任何英语写作更清楚地解释它的作用:

> zippers "abcd"
[("",'a',"bcd"),("a",'b',"cd"),("ba",'c',"d"),("cba",'d',"")]
换句话说,它提供了一种依次选择列表中每个元素的方法,给出了“剩余”“在选择点之前和之后是什么。有了这个工具,我们的计划是:我们将不确定地选择一个
字符串作为等号,再次检查我们是否首先得到了等号,然后从其他字符串中清除等号。因此:

fixEquals ss = do
    (prefix, s, suffix) <- zippers ss
    guard ('=' `elem` s)
    return (reverse (deleteEquals prefix) ++ ["="] ++ deleteEquals suffix)

deleteEquals = map (filter ('='/=))
太好了!但这只是真正生成方程的垫脚石,对吗?事实证明,一步到位,跳过中间环节并不难。让我们这样做:

equations ss = do
    (prefixes, s, suffixes) <- zippers ss
    guard ('=' `elem` s)
    prefix <- mapM (filter ('='/=)) (reverse prefixes)
    suffix <- mapM (filter ('='/=)) suffixes
    return (prefix ++ "=" ++ suffix)

要实现您的目标,最简单的方法是创建所有组合,并过滤有意义的组合:

Prelude> test = ["12", "34=", "56=", "78"]
Prelude> sequence test
["1357","1358","1367","1368","13=7","13=8","1457","1458","1467","1468","14=7","14=8","1=57","1=58","1=67","1=68","1==7","1==8","2357","2358","2367","2368","23=7","23=8","2457","2458","2467","2468","24=7","24=8"
Prelude> filter ((1==).length.filter('='==)) $ sequence test
["13=7","13=8","14=7","14=8","1=57","1=58","1=67","1=68","23=7","23=8","24=7","24=8","2=57","2=58","2=67","2=68"]
您指出了缺点:假设我们有以下字符串列表:
[“=”、“=”、“0123456789”、“0123456789”]
。我们将生成100个组合并将它们全部删除

您可以将组合视为一棵树。对于
[“12”,“34”]
,您有:

  /  \
 1    2
/ \  / \
3 4  3 4
您可以修剪树:当路径上有两个
=
时,只需忽略子树即可。 让我们尝试一下。首先,一个简单的
组合
函数:

Prelude> :set +m
Prelude> let combinations :: [String] -> [String]
Prelude|     combinations [] = [""]
Prelude|     combinations (cs:ts) = [c:t | c<-cs, t<-combinations ts]
Prelude|
Prelude> combinations test
["1357","1358","1367","1368","13=7","13=8","1457","1458","1467","1468","14=7","14=8","1=57","1=58","1=67","1=68","1==7","1==8","2357","2358","2367","2368","23=7","23=8","2457","2458","2467","2468","24=7","24=8", ...]
  • 我们使用
    p
    作为路径上
    =
    符号的新编号:如果
    p>1
    ,则删除子树
  • 如果
    n
    为零,则路径中没有任何
    =
    符号,请删除该组合

您可以使用变量
n
来存储更多信息,例如最后一个字符的类型(以避免
+*
序列)。

过滤器(非空)更优雅-谢谢。但仍然有一个问题,它创建了4个列表,然后扔掉了2个,这是我宁愿避免的。谢谢@Lorenzo-当然更清楚地解决了我的问题。你迫不及待地建议我可能会找到一个更好的数据结构-对我可能探索的类型有什么想法吗?@Adahus你想要的真正问题是什么o解算/正在处理?@Elmex80s我的整个程序涉及找到适合某些掩码的算术方程式。我尝试生成所有字符序列,然后进行解析,看看它们是否是平衡方程式,但效率太低,因为我尝试加载完全无效的字符串(例如“999+=”).我现在正在实施一种新的方法,需要上面讨论的例行程序。我对效率的提高很有信心,因为我以前在Swift中已经实施了很多,但我想将其转化为Haskell,在我学习Haskell的过程中,似乎最好是自下而上进行构建。谢谢你的提问。是的,Haskell中的方法通常是联合使用的与您在命令式编程中使用的完全不同。我不能真正理解您想要做什么,但我要说的是,将
=
放在字符串中似乎是不正确的。也许您可以看看monad,或者创建自己的数据类型,让Swift相信它确实进入了函数式编程领域,也成为了命令式核心。我t当然有map、filter、reduce,并且可以扩展为可组合性(如果感兴趣,可以搜索“swiftpoint Free”).事实上,我是通过从Swift到PointFree再到Haskell的一个线程来到Haskell的。)但是的-Haskell是纯函数型的,因此肯定需要一种不同的方法。最好将
fixEquals
的Inner列表理解放在一个单独的函数中。这些可能的字符序列是约束的原始形式还是ar它们来自于一些更…合理的…约束?如果是后者,原始约束是什么?我在做算术拼图,答案是方程。有时我直接说一些符号是什么。这意味着我可能知道一个方程是“49°=?”3我需要找到所有符合这个模板的方程,在这种情况下
> fixEquals ["12", "34=", "56=", "78"]
[["12","=","56","78"],["12","34","=","78"]]
equations ss = do
    (prefixes, s, suffixes) <- zippers ss
    guard ('=' `elem` s)
    prefix <- mapM (filter ('='/=)) (reverse prefixes)
    suffix <- mapM (filter ('='/=)) suffixes
    return (prefix ++ "=" ++ suffix)
> equations ["12", "34=", "56=", "78"]
["1=57","1=58","1=67","1=68","2=57","2=58","2=67","2=68","13=7","13=8","14=7","14=8","23=7","23=8","24=7","24=8"]
Prelude> test = ["12", "34=", "56=", "78"]
Prelude> sequence test
["1357","1358","1367","1368","13=7","13=8","1457","1458","1467","1468","14=7","14=8","1=57","1=58","1=67","1=68","1==7","1==8","2357","2358","2367","2368","23=7","23=8","2457","2458","2467","2468","24=7","24=8"
Prelude> filter ((1==).length.filter('='==)) $ sequence test
["13=7","13=8","14=7","14=8","1=57","1=58","1=67","1=68","23=7","23=8","24=7","24=8","2=57","2=58","2=67","2=68"]
  /  \
 1    2
/ \  / \
3 4  3 4
Prelude> :set +m
Prelude> let combinations :: [String] -> [String]
Prelude|     combinations [] = [""]
Prelude|     combinations (cs:ts) = [c:t | c<-cs, t<-combinations ts]
Prelude|
Prelude> combinations test
["1357","1358","1367","1368","13=7","13=8","1457","1458","1467","1468","14=7","14=8","1=57","1=58","1=67","1=68","1==7","1==8","2357","2358","2367","2368","23=7","23=8","2457","2458","2467","2468","24=7","24=8", ...]
Prelude> let combinations' :: [String] -> Int -> [String]
Prelude|     combinations' [] n= if n==1 then [""] else []
Prelude|     combinations' (cs:ts) n = [c:t | c<-cs, let p = n+(fromEnum $ c=='='), p <= 1, t<-combinations' ts p]
Prelude|
Prelude> combinations' test 0
["13=7","13=8","14=7","14=8","1=57","1=58","1=67","1=68","23=7","23=8","24=7","24=8","2=57","2=58","2=67","2=68"]