【问题标题】:Using foldl on 2dimentional lists in Haskell在 Haskell 的二维列表上使用 foldl
【发布时间】:2014-11-17 08:55:40
【问题描述】:

我正在尝试实现一个函数,该函数以 bfs 顺序收集 OrdTree 中的所有值,但我也很难做到这一点。到目前为止,这是我带来的:

data OrdTree a = OrdTree a [OrdTree a]  deriving (Show)

bfsTree ::  OrdTree a ->  [a]
bfsTree tree = 
    let 
        use2DList level list (OrdTree a []) = (list!!level)++[a]
        use2DList currentLevel list (OrdTree a (branches)) =
            foldl 
                (\tree theList -> use2DList (currentLevel+1) theList tree) 
                ((list!!currentLevel)++[a]) 
                branches 
    in 
        concat (use2DList 0 [] tree)

显然,我遇到了一堆我无法弄清楚的错误。

  1. 我认为既然它似乎在范围内,我应该能够在 foldl 中使用currentLevel 变量(特别是在匿名函数中),是吗?可以吗?还是只需要使用-> 左侧指定的操作数?
  2. 我知道这些东西是不可变的,但是列表列表..如果我将一个值附加到列表中的一个列表中,它会更改它,对吗?但是这样的代码返回了什么:(list!!position)++[value]
  3. 如果我尝试像这样咖喱它会更好(如果我没猜错的话):
    foldl (use2DList (currentLevel+1)) (list!!currentLevel)++[a]) branches 还是更糟?

对不起,如果文字太多,我真的对这些东西感到困惑。

【问题讨论】:

    标签: list haskell fold


    【解决方案1】:

    这就是你说的 bfs 订单的意思吗?

    val (OrdTree a _) = a
    children (OrdTree _ ts) = ts
    
    bfs :: [OrdTree a] -> [a]
    bfs ts = (map val ts) ++ bfs (concatMap children ts)
    

    请注意,我写bfs 是为了获取Ordtree a 的列表,而不仅仅是一棵树。

    • bfs 的参数是当前级别的树列表。
    • map val ts 是当前级别的值
    • concatMap children ts是下一层的所有树

    更新:这是我对您的代码所做的一些分析。

    首先它没有编译,所以我开始删除代码,直到编译器停止抱怨,我到达:

    data OrdTree a = OrdTree a [OrdTree a]  deriving (Show)
    
    bfsTree tree =
          let
              use2DList level list (OrdTree a []) = [a] ++ (list!!level)
          in concat (use2DList 0 [] tree)
    

    注意bfsTree 上没有类型签名 - 我们将让 ghc 告诉我们签名是什么,它会响应:

    bfsTree :: OrdTree [a] -> [a]
    

    这显然不对 - 我们想要OrdTree a -> [a]。原来,罪魁祸首是concat,所以显然代码应该是这样的:

    bfsTree tree =
          let
              use2DList level list (OrdTree a []) = [a] ++ (list!!level)
          in use2DList 0 [] tree
    

    现在我们可以问use2DList的类型签名是什么,ghc(真的是ghc-mod)会回复:

    use2DList :: Int -> [[a]] -> OrdTree a -> [a]
    

    现在我们必须弄清楚foldl 应该是什么。 foldl 有签名:

    foldl :: (s -> t -> s) -> s -> [t] -> s
    

    显然我们是折叠分支,即t ~ OrdTree a,而use2DList的返回类型是[a],所以s ~ [a],所以foldl的第一个参数应该是这样的:

    (\listofa tree -> ...)
    

    自从你的函数开始(\tree theList -> ...) 看起来你必须交换参数。就我所知。

    主要的建议是:

    • 使用 GHC 和类型系统来帮助您捕获错误;为所有定义添加类型签名 - 包括辅助函数
    • 学习使用 ghci 中的:t 命令来查询推断的函数类型
    • 为 vim 使用 ghcmod-vim 插件或为 emacs 使用 haskell-mode 插件,以便您可以在程序中查询子表达式

    【讨论】:

    • 哇,很酷,这就是解决方案,谢谢。但是有什么办法让它像我尝试的那样工作吗?您能否指出最糟糕的时刻,这样我就不会再这样做了?
    • 我看了你的代码,但是有太多的类型错误,所以我决定只发布这个。此外,您没有函数valchildren(或等效的模式匹配)这一事实是一个很大的危险信号,表明存在非常错误的情况。也许你可以解释一下你的算法是如何工作的。
    • 对不起,我没有意识到这很糟糕。我想将值添加到二维列表中,以便每个级别的值都位于该列表中的某个位置,这意味着像 [[topValue]、[kidsOfFirstLayer]、[kidsOfSecond]、[etc]] 之类的东西,然后这样做我传递了currentValue 来指示应该附加到哪个列表本地元素。而且我确实意识到,显然我在算法方面仍然很糟糕。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-04
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2019-05-29
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多