Haskell:简化由:t提供的可变向量类型签名

Haskell:简化由:t提供的可变向量类型签名,haskell,Haskell,我可以编译以下代码,但是当我使用:tqsort时,我得到了下面的长而复杂的类型签名。然而,当我加上它时,程序将不再进行类型检查,如果没有额外的导入,请参阅下面的答案。我对定义类型的最佳猜测也粘贴在下面,但类型检查和单子不都让人困惑 好的,所以我可以添加一个额外的导入语句和it类型检查,但我从未在任何在线发布的代码上看到过如此复杂的代码签名。所以我的问题是: 我可以删除Ord t和exhange Int来表示t,并进行检查,但如何用ST或IO替换Control.Monad.Primitive.Pr

我可以编译以下代码,但是当我使用:tqsort时,我得到了下面的长而复杂的类型签名。然而,当我加上它时,程序将不再进行类型检查,如果没有额外的导入,请参阅下面的答案。我对定义类型的最佳猜测也粘贴在下面,但类型检查和单子不都让人困惑

好的,所以我可以添加一个额外的导入语句和it类型检查,但我从未在任何在线发布的代码上看到过如此复杂的代码签名。所以我的问题是:

我可以删除Ord t和exhange Int来表示t,并进行检查,但如何用ST或IO替换Control.Monad.Primitive.PrimMonad,以及如何使用v。 我正试图编写一个例子来说明如何使用这些单子,相比之下,对于数组,qsort的类型签名是一个更易于管理的qsort::STArray s Int->Int->Int->Int->ST s。对于那些想理解s的人来说,在线上有很多解释,所有的解释都略高于我的理解——我明白,让类型检查器本身来防止作者编写代码时,Monad中的数据泄漏,从而导致杂质,这是一个聪明的把戏

import Control.Monad
import Control.Monad.ST
import qualified Data.Vector as V
import qualified Data.Vector.Generic.Mutable as MV

main = do
    lines <- BS.lines `fmap` BS.readFile "10.txt"
    let
        inputData = Prelude.map (maybe (error "can't read Int") fst . BS.readInt) lines
        initialImmutableVector = V.fromList inputData

    print $ runST $ do
        state <- V.thaw initialImmutableVector
        qsort state 0 (Prelude.length inputData - 1)
        frozen <- V.freeze state
        return frozen

--qsort :: (MV.MVector s Int) -> Int -> Int -> ST s ()
--qsort
--  :: (Ord t, Control.Monad.Primitive.PrimMonad m, MV.MVector v t) =>
--     v (Control.Monad.Primitive.PrimState m) t -> Int -> Int -> m ()
qsort vec min mx =
    if mx - min < 1 then
        return ()

    else do
        p <- MV.read vec min
        final_i <- foldM (partitioner p) (min+1) [(min+1)..mx]
        swap min (final_i - 1)
        qsort vec min     (final_i-2)
        qsort vec final_i mx     

    where
        swap i j = do
            vec_i <- MV.read vec i
            vec_j <- MV.read vec j
            MV.write vec i vec_j
            MV.write vec j vec_i

        partitioner p i acc = do
            vec_acc <- MV.read vec acc
            if vec_acc > p then
                return i
            else do
                swap i acc
                return $ i+1

看起来您上次的尝试是正确的,但可能您没有导入所需的所有内容?按原样粘贴的代码无论是否使用类型签名都不会编译。这是一个非常轻微的修改版本,适用于我的GHC 7.8.3:

import Control.Monad
import Control.Monad.ST
import qualified Data.Vector as V
import qualified Data.Vector.Generic.Mutable as MV
import qualified Data.ByteString.Char8 as BS
import Control.Monad.Primitive (PrimState, PrimMonad)
import Prelude hiding (lines, min)

main :: IO ()
main = do
    lines <- BS.lines `fmap` BS.readFile "10.txt"
    let
        inputData = map (maybe (error "can't read Int") fst . BS.readInt) lines
        initialImmutableVector = V.fromList inputData

    print $ runST $ do
        state <- V.thaw initialImmutableVector
        qsort state 0 (Prelude.length inputData - 1)
        frozen <- V.freeze state
        return frozen

qsort :: (Ord t, PrimMonad m, MV.MVector v t)
      => v (PrimState m) t -> Int -> Int -> m ()
qsort vec min mx =
    if mx - min < 1 then
        return ()

    else do
        p <- MV.read vec min
        final_i <- foldM (partitioner p) (min+1) [(min+1)..mx]
        swap min (final_i - 1)
        qsort vec min     (final_i-2)
        qsort vec final_i mx     

    where
        swap i j = do
            vec_i <- MV.read vec i
            vec_j <- MV.read vec j
            MV.write vec i vec_j
            MV.write vec j vec_i

        partitioner p i acc = do
            vec_acc <- MV.read vec acc
            if vec_acc > p then
                return i
            else do
                swap i acc
                return $ i+1

看起来您上次的尝试是正确的,但可能您没有导入所需的所有内容?按原样粘贴的代码无论是否使用类型签名都不会编译。这是一个非常轻微的修改版本,适用于我的GHC 7.8.3:

import Control.Monad
import Control.Monad.ST
import qualified Data.Vector as V
import qualified Data.Vector.Generic.Mutable as MV
import qualified Data.ByteString.Char8 as BS
import Control.Monad.Primitive (PrimState, PrimMonad)
import Prelude hiding (lines, min)

main :: IO ()
main = do
    lines <- BS.lines `fmap` BS.readFile "10.txt"
    let
        inputData = map (maybe (error "can't read Int") fst . BS.readInt) lines
        initialImmutableVector = V.fromList inputData

    print $ runST $ do
        state <- V.thaw initialImmutableVector
        qsort state 0 (Prelude.length inputData - 1)
        frozen <- V.freeze state
        return frozen

qsort :: (Ord t, PrimMonad m, MV.MVector v t)
      => v (PrimState m) t -> Int -> Int -> m ()
qsort vec min mx =
    if mx - min < 1 then
        return ()

    else do
        p <- MV.read vec min
        final_i <- foldM (partitioner p) (min+1) [(min+1)..mx]
        swap min (final_i - 1)
        qsort vec min     (final_i-2)
        qsort vec final_i mx     

    where
        swap i j = do
            vec_i <- MV.read vec i
            vec_j <- MV.read vec j
            MV.write vec i vec_j
            MV.write vec j vec_i

        partitioner p i acc = do
            vec_acc <- MV.read vec acc
            if vec_acc > p then
                return i
            else do
                swap i acc
                return $ i+1

导入函数不会导入其类型。如果代码显式引用该类型,则必须导入该类型。只要代码没有显式引用这些类型,就可以使用导入的函数,而无需导入其参数或返回值的类型。一旦开始显式使用类型或类,就必须导入它们,这是有意的。

导入function-s不会导入它们的类型。如果代码显式引用该类型,则必须导入该类型。只要代码没有显式引用这些类型,就可以使用导入的函数,而无需导入其参数或返回值的类型。一旦你开始显式地使用类型或类,你就必须导入它们,这是有意的。

是的,但是更可读的版本是什么呢?我从来没有在书籍或web教程中见过这么复杂的类型签名?除非你专门研究IO,否则它的可读性不会比这更高。ST执行一个秩-2类型技巧,以确保ST操作的状态无法从程序的其余部分访问,因此其类型签名会变得复杂,无论是显式forall还是此typeclass。是的,但更可读的版本是什么?我在书籍或web教程中从未见过如此复杂的类型签名?嗯,除非您专门研究IO,否则它的可读性不会比这更高。ST执行一个秩2类型技巧,以确保ST操作的状态无法从程序的其余部分访问,因此其类型签名会因显式forall或此类型类而变得复杂。与某些ml语言不同,在Haskell中,您不能简单地引用值或类型,任何真正完全限定的标识符,您必须首先导入它。请注意,Data.Vector.X.Mutable模块以您的方式导出定义的MV.swap,但带有边界检查。最好是废弃Data.Vector并使用Data.Vector.Generic,这样整个模块都是泛型的,或者对可变和不可变向量都使用unbox,这样整个模块都专用于特别有价值的unbox类型。您通常会导入一对匹配的模块,其中一个附加了.Mutable。Data.Vector本身没有其他的好。我想让你不安的是,除了类型类之外,primitive和Vector使用类型函数,这确实增加了另一层复杂性。@Arthur我认为你是对的。我不能一次了解所有内容,所以是否有一个更简单的类型签名更容易阅读,可以提供一个可变的Int向量?如果省略泛型,将限定的Data.vector.mutable作为MV导入,那么您可以编写qsort::MV.STVector s Int->Int->Int->Int->ST s或qsort::MV.IOVector Int->Int->Int->IO。后者不适合main,因为它使用runST。前者与qsort::MV.MVector s Int->Int->Int->Int->ST s相同,这是原始的“短”签名。与某些ml语言不同,在Haskell中,您不能简单地引用值或类型,任何真正完全限定的标识符,您必须首先导入它。请注意,Data.Vector.X.Mutable模块以您的方式导出定义的MV.swap,但带有边界检查。这可能是最好的选择
然后废弃Data.Vector并使用Data.Vector.Generic,这样整个模块是泛型的,或者对可变和不可变向量使用Unboxed,这样整个模块专用于特别有价值的Unboxed类型。您通常会导入一对匹配的模块,其中一个附加了.Mutable。Data.Vector本身没有其他的好。我想让你不安的是,除了类型类之外,primitive和Vector使用类型函数,这确实增加了另一层复杂性。@Arthur我认为你是对的。我不能一次了解所有内容,所以是否有一个更简单的类型签名更容易阅读,可以提供一个可变的Int向量?如果省略泛型,将限定的Data.vector.mutable作为MV导入,那么您可以编写qsort::MV.STVector s Int->Int->Int->Int->ST s或qsort::MV.IOVector Int->Int->Int->IO。后者不适合main,因为它使用runST。前者与qsort::MV.MVector s Int->Int->Int->ST s相同,您可以使用原始的“short”签名。