【问题标题】:Haskell arrays are too strict?Haskell 数组太严格了?
【发布时间】:2013-03-06 17:33:20
【问题描述】:

我在 Haskell 中实现 Smith-Waterman 算法,但出现运行时错误:<<loop>>

在我的实现中,我尝试使用 Haskell 的惰性特性,因此我使用不可变数组 resarray 来存储惰性和递归存根,这些存根也引用数组本身(在依赖链中 resarray 取决于在 zippedList 这取决于 cellDef 这取决于 cell 这取决于 resarray )。每个单元格都指向一个索引较小的单元格,因此计算应该是可行的……尽管它的行为方式并非如此。

作为概念证明,我在 ghci 中尝试了以下方法:

let arr = listArray (0,3) [0, arr ! 0, arr ! 1, arr ! 2 ]

它奏效了。然而,由于某种未知原因,我的较长计算最终变得严格。

这是我的代码(完整版本,连同一个测试脚本,是here):

buildSWArray:: 
    WordSequence ->
    WordSequence ->
    SWMatrix
buildSWArray ws1 ws2 = let
        rows = arrLen ws1
        cols = arrLen ws2
        im = matToLinearIndex rows cols
        mi = linToMatIndex rows cols
        totsize = rows * cols
        ixarr = [0 .. (totsize-1)]
        cell i j 
            | i < 0 || j < 0 = 0
        cell i j  = 
            resarr !  (im i j ) 
        cellDef k | k == 0 = (None,0)
        cellDef k = 
            let
                (i,j) = mi k
                upwards = cell (i-1) j
                leftwards = cell i (j-1)
                diag = cell (i-1) (j-1) 
                -- One up if match, -5 if not match
                c = if ws1 ! i == ws2 ! j then 1 else (-5)
                hi = maximum [ 0, diag + c, upwards - 3, leftwards - 3]
            in 
                -- Dirty way of guessing which one was picked up
                case hi of 
                   hi | hi == 0  -> ( None, 0)
                   hi | hi == upwards - 3 -> ( Upwards, hi)
                   hi | hi == leftwards - 3 -> ( Leftwards, hi )
                   hi | hi == diag + c -> (Diag, hi )
        zippedList = [ cellDef k | k <- ixarr ]
        resarr =  IA.listArray (0,(totsize - 1)) [ score | (way,score) <- zippedList ]
        wayarr = IA.listArray (0,(totsize - 1)) [ way | (way,score) <- zippedList ]
    in 
        SWMatrix rows cols wayarr resarr

我该如何解决?

【问题讨论】:

  • 我猜你没能正确地“打结”。检查您的基本情况并验证您的假设,即您的递归参数正在减少。

标签: haskell


【解决方案1】:

你对模式匹配很严格,

resarr =  IA.listArray (0,(totsize - 1)) [ score | (way,score) <- zippedList ]
wayarr = IA.listArray (0,(totsize - 1)) [ way | (way,score) <- zippedList ]

强制在构造时读取数组元素,这是行不通的。

简单示例:

module LazyArr where

import Data.Array.IArray

test :: Int -> (Array Int Int, Array Int Int)
test n =
    let zippedList = map foo [0 .. n]
        foo :: Int -> (Int,Int)
        foo i
            | i == 0 = (0,0)
            | arrOne ! (i-1) < arrTwo ! (i-1) = (2,1)
            | even i = (i,arrTwo ! (i-1))
            | otherwise = (arrOne ! (i-1),i)
        arrOne = listArray (0,n) $ map fst zippedList -- [a | (a,b) <- zippedList]
        arrTwo = listArray (0,n) $ map snd zippedList -- [b | (a,b) <- zippedList]
    in (arrOne, arrTwo)

有效,但使用列表推导而不是 map fst resp。 map snd,循环播放。

所以使用惰性版本 map fst zippedListmap snd zippedList 应该可以工作(列表推导中的惰性模式 [way | ~(way,score) &lt;- zippedList] 也应该如此),至少我在依赖项中没有看到更多问题。

通过对这对的模式匹配,cellDef k 必须被评估得足够远,才能看到顶级构造函数确实是 (,)。为此,必须确定所采用的分支,这需要检查早期元素的包含值。但是在创建数组时,这些还不能获得。

每个单元格引用索引较小的单元格,因此计算应该是可行的

然而,这并不重要。您所需要的只是没有依赖循环,并且每条链都会导致定义的基本案例。

【讨论】:

  • 太棒了!现在解决了。总而言之,令人惊讶的是,我没有从 Haskell 运行时得到任何更糟的结果,而且只是一个非常测量的 >
  • 但是为什么fstsnd 不在其源代码中使用惰性模式?
  • @is7s 在那里使用惰性模式是没有意义的。在需要其结果之前不会评估对fst 的调用[除非优化器发现它可以并且应该尽早评估它]。当需要它的结果时,fst 必须解构它的参数。
猜你喜欢
  • 1970-01-01
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
  • 2020-08-07
  • 2014-12-14
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
相关资源
最近更新 更多