为什么我会得到" xx不在范围错误"这个haskell代码?

时间:2015-04-28 15:08:50

标签: haskell

我编写了这样的代码,但在编译说" xx不在范围"时会出错。

test x =
    let xx = 2 * x
     in result 
       where result = replicate xx 3

我知道我可以使用in replicate xx 3修复它,但是,上面的代码只是一个演示,我正在处理的真实代码如下:

nthElement :: (Ord b)=>(a->b)->Int->[a]->[a]
nthElement _ _ []  = []
nthElement _ _ [x] = [x]
nthElement op k vals@(x:xs)
    | k > (length vals) = vals
    | otherwise = let left    = [p | p<-vals, (op p) < (op x)]
                      midd    = [p | p<-vals, (op p) == (op x)]
                      right   = [p | p<-vals, (op p) > (op x)]
                      leftLen = length left
                      middLen = length midd
                   in result where result | leftLen >= k             = (nthElement op k left) ++ midd ++ right
                                          | (leftLen + middLen) >= k = left ++ midd ++ right
                                          | otherwise                = left ++ midd ++ nthElement op (k-middLen-leftLen) right

似乎如果我不使用where子句,我必须使用深层嵌套,如下所示:

nthElement :: (Ord b)=>(a->b)->Int->[a]->[a]
nthElement _ _ []  = []
nthElement _ _ [x] = [x]
nthElement op k vals@(x:xs)
    | k > (length vals) = vals
    | otherwise = let left   = [p | p<-vals, (op p) < (op x)]
                      midd   = [p | p<-vals, (op p) == (op x)]
                      right  = [p | p<-vals, (op p) > (op x)]
                      leftLen = length left
                      middLen = length midd
                   in if leftLen >= k 
                         then (nthElement op k left) ++ midd ++ right
                         else if (leftLen + middLen) >= k 
                                then left ++ midd ++ right
                                else left ++ midd ++ nthElement op (k-middLen-leftLen) right

那么,我怎么能改变我的代码来修复编译错误以及避免使用嵌套的if?

2 个答案:

答案 0 :(得分:3)

您应该将此代码更多地视为

test x = {
    let {
        xx = 2 * x
    } in {
        result
    }
} where {
    result = replicate xx 3
}

而不是

test x = {
    let {
        xx = 2 * x
    } in {
        result where {
            result = replicate xx 3
        }
    }
}

where子句涵盖函数体的整个定义,并且只能使用在函数体外定义的名称(testtest本身的参数)。解决此问题的最佳方法是将所有定义移至letwhere。对于您的情况,您可能希望将它们全部移动到let

test x =
    let xx = 2 * x
        result = replicate xx 3
    in result

或者您的实际使用案例:

nthElement :: (Ord b) => (a -> b) -> Int -> [a] -> [a]
nthElement _ _ []  = []
nthElement _ _ [x] = [x]
nthElement op k vals@(x:xs)
    | k > (length vals) = vals
    | otherwise = let left    = [p | p<-vals, (op p) < (op x)]
                      midd    = [p | p<-vals, (op p) == (op x)]
                      right   = [p | p<-vals, (op p) > (op x)]
                      leftLen = length left
                      middLen = length midd
                      result | leftLen >= k             = (nthElement op k left) ++ midd ++ right
                             | (leftLen + middLen) >= k = left ++ midd ++ right
                             | otherwise                = left ++ midd ++ nthElement op (k-middLen-leftLen) right
                   in result

但是,当这个从页面的侧面行进时,我会稍微重构一下,只使用一个警卫和where

nthElement :: (Ord b) => (a -> b) -> Int -> [a] -> [a]
nthElement _ _ []  = []
nthElement _ _ [x] = [x]
nthElement op k vals@(x:xs)
    | k > length vals  = vals
    | k <= leftLen     = nth k left ++ midd ++        right
    | k <= leftMiddLen =       left ++ midd ++        right
    | otherwise        =       left ++ midd ++ nth kR right
    where
        opx         = op x
        left        = [p | p <- vals, op p <  opx]
        midd        = [p | p <- vals, op p == opx]
        right       = [p | p <- vals, op p >  opx]
        leftLen     = length left
        middLen     = length midd
        leftMiddLen = leftLen + middLen
        nth         = nthElement op
        kR          = k - leftMiddLen

98%这只是风格,你可能不喜欢这种方式,但我觉得它更容易阅读。特别是,我会说2%不仅仅是风格将警卫折叠到一个单一的水平,它使你的意图更清晰。由于Haskell很懒,所以在实际使用该值之前,您也不必担心计算任何东西。

答案 1 :(得分:1)

nthElement :: (Ord b)=>(a->b)->Int->[a]->[a]
nthElement _ _ []  = []
nthElement _ _ [x] = [x]
nthElement op k vals@(x:xs)
    | k > (length vals) = vals
    | otherwise = let left    = [p | p<-vals, (op p) < (op x)]
                      midd    = [p | p<-vals, (op p) == (op x)]
                      right   = [p | p<-vals, (op p) > (op x)]
                      leftLen = length left
                      middLen = length midd
                      result | leftLen >= k             = (nthElement op k left) ++ midd ++ right
                             | (leftLen + middLen) >= k = left ++ midd ++ right
                             | otherwise                = left ++ midd ++ nthElement op (k-middLen-leftLen) right
                   in result