过去一两个月我一直在学习Haskell,最近解决了this编码问题。另外的挑战是在没有额外空间和线性时间的情况下完成任务,我认为这不可能以纯粹的功能方式完成,所以我自然而然地发现了ST monad,我认为这将是一个很好的机会了解更多信息。无论如何,这是我写的代码:
module FindDuplicates where
import Control.Monad (foldM)
import Control.Monad.ST
import Data.Array.ST
xs = [4,3,2,7,8,2,3,1] :: [Int]
findDuplicates :: [Int] -> ST s [Int]
findDuplicates xs = do
arr <- newListArray (1, length xs) xs :: ST s (STArray s Int Int)
let go :: [Int] -> Int -> ST s [Int]
go acc i = do x <- abs <$> readArray arr i
y <- readArray arr x
if y < 0
then return (x:acc)
else do writeArray arr x (-y)
return acc
foldM go [] [1..length xs]
这个想法是使用1≤a[i]≤n的前提条件,并且每个元素最多出现2次。但是代码给了我以下错误。
FindDuplicates.hs:14:36:
No instance for (MArray (STArray s) Int (ST s1))
arising from a use of ‘readArray’
In the second argument of ‘(<$>)’, namely ‘readArray arr i’
In a stmt of a 'do' block: x <- abs <$> readArray arr i
In the expression:
do { x <- abs <$> readArray arr i;
y <- readArray arr x;
if y < 0 then
return (x : acc)
else
do { writeArray arr x (- y);
.... } }
我希望有人能指出我正确的方向!
答案 0 :(得分:4)
在No instance for (MArray (STArray s) Int (ST s1))
中,最重要的是要注意两个不同的类型变量s
和s1
。除非这两个类型变量相同,否则没有MArray
的实例。这是ST
对外部纯接口有效的重要部分。
编译器认为涉及两个不同类型变量的原因是您在go
上放置了类型签名。该签名中的类型变量s
与s
签名中的类型变量findDuplicates
不同。这是Haskell类型签名规则的固有部分 - 任何特定签名中的类型变量与任何其他签名中的类型变量无关。
解决此问题的最简单方法是从go
中删除签名。类型推断将获得正确的类型。
如果您想获得更高级,您可以使用ScopedTypeVariables
扩展名允许go
上的签名与封闭定义共享类型变量:
{-# LANGUAGE ScopedTypeVariables #-}
module FindDuplicates where
import Control.Monad (foldM)
import Control.Monad.ST
import Data.Array.ST
xs = [4,3,2,7,8,2,3,1] :: [Int]
findDuplicates :: forall s. [Int] -> ST s [Int]
findDuplicates xs = do
arr <- newListArray (1, length xs) xs :: ST s (STArray s Int Int)
let go :: [Int] -> Int -> ST s [Int]
go acc i = do x <- abs <$> readArray arr i
y <- readArray arr x
if y < 0
then return (x:acc)
else do writeArray arr x (-y)
return acc
foldM go [] [1..length xs]
顶部的LANGUAGE
pragma启用扩展程序。要使用扩展,您需要在forall
的定义中指定类型变量。 (忘记这样做是ScopedTypeVariables
无法工作的最常见原因。)
在findDuplicates
类型中执行此操作后,它会将s
存储在整个定义的范围内。在s
类型中查找类型变量go
时,它不再将其视为新类型变量,而是使其与封闭上下文中的类型s
匹配。