我是Haskell的新手。我对命令式语言非常满意,但对功能不满意。 Haskell是我的第一门功能语言。
我正在尝试找出如何获取列表中最小元素的索引,其中最小元素是由我定义的。
让我通过示例进行解释。
例如:
功能签名 minList :: x-> [x]
id
这应该返回1,因为at列表[1]为3。由于3是x(= 2)之后的最小元素,因此返回1。
let x = 2
let list = [2,3,5,4,6,5,2,1,7,9,2]
minList x list --output 1 <- is index
它应该返回9,因为在list [9]处为2,而2是1以后的最小元素。x = 1由我定义。
到目前为止我尝试过的。
let x = 1
let list = [3,5,4,6,5,2,1,7,9,2]
minList x list -- output 9 <- is index
加载文件时出现此错误
minListIndex :: (Ord a, Num a) => a -> [a] -> a
minListIndex x [] = 0
minListIndex x (y:ys)
| x > y = length ys
| otherwise = m
where m = minListIndex x ys
当我这样修改功能时
• Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ is a rigid type variable bound by
the type signature for:
minListIndex :: forall a. (Ord a, Num a) => a -> [a] -> a
at myFile.hs:36:17
• In the expression: 1 + length ys
In an equation for ‘minListIndex’:
minListIndex x (y : ys)
| x > y = 1 + length ys
| otherwise = 1 + m
where
m = minListIndex x ys
• Relevant bindings include
m :: a (bound at myFile.hs:41:19)
ys :: [a] (bound at myFile.hs:38:19)
y :: a (bound at myFile.hs:38:17)
x :: a (bound at myFile.hs:38:14)
minListIndex :: a -> [a] -> a (bound at myFile.hs:37:1)
我再次加载文件,然后编译并运行该文件,但不需要输出。
有什么问题
minListIndex :: (Ord a, Num a) => a -> [a] -> a
minListIndex x [] = 0
minListIndex x (y:ys)
| x > y = 2 -- <- modified...
| otherwise = 3 -- <- modifiedd
where m = minListIndex x ys
?
简而言之:基本上,我想找到最小元素的索引,但要比我在参数/函数签名中定义的x高。
谢谢您的帮助!
答案 0 :(得分:3)
minListIndex :: (Ord a, Num a) => a -> [a] -> a
问题是您试图返回通用类型a
的结果,但实际上它是列表中的索引。
假设您正在尝试评估函数以获取双打列表。在这种情况下,编译器应将函数的类型实例化为Double -> [Double] -> Double
,这是毫无意义的。
实际上,编译器会注意到您返回的是从列表长度得出的内容,并警告您无法将泛型类型a
与具体的Int
相匹配。
length ys
返回Int
,因此您可以尝试以下操作:
minListIndex :: Ord a => a -> [a] -> Int
关于您的原始问题,看来您无法通过简单的递归来解决。考虑使用accumulator定义辅助递归函数。在您的情况下,可以是一对(min_value_so_far, its_index)
。
答案 1 :(得分:2)
首先,我将索引类型与列表元素类型完全分开。没有明显的理由使它们相同。我将使用BangPatterns
扩展名来避免空间泄漏而不必使用过多的符号。通过在文件的顶部添加{-# language BangPatterns #-}
来启用该功能。我还将导入Data.Word
来访问Word64
类型。
有两个阶段:首先,找到给定元素的索引(如果存在),然后找到该点之外的其余列表。然后,找到尾巴最小值的索引。
-- Find the 0-based index of the first occurrence
-- of the given element in the list, and
-- the rest of the list after that element.
findGiven :: Eq a => a -> [a] -> Maybe (Word64, [a])
findGiven given = go 0 where
go !_k [] = Nothing --not found
go !k (x:xs)
| given == xs = Just (k, xs)
| otherwise = go (k+1) xs
-- Find the minimum (and its index) of the elements of the
-- list greater than the given one.
findMinWithIndexOver :: Ord a => a -> [a] -> Maybe (Word64, a)
findMinWithIndexOver given = go 0 Nothing where
go !_k acc [] = acc
go !k acc (x : xs)
| x <= given = go (k + 1) acc xs
| otherwise
= case acc of
Nothing -> go (k + 1) (Just (k, x)) xs
Just (ix_min, curr_min)
| x < ix_min = go (k + 1) (Just (k, x)) xs
| otherwise = go (k + 1) acc xs
您现在可以将这些功能放在一起,以构建您想要的功能。如果您想要一个一般的Num
结果而不是Word64
,则可以在最后使用fromIntegral
。为什么要使用Word64
?与Int
或Word
不同,它(实际上)保证在任何合理的时间内都不会溢出。这可能比直接使用Integer
或Natural
之类的东西要快得多。
答案 2 :(得分:1)
您的功能可以由现成的部件构成
import Data.Maybe (listToMaybe)
import Data.List (sortBy)
import Data.Ord (comparing)
foo :: (Ord a, Enum b) => a -> [a] -> Maybe b
foo x = fmap fst . listToMaybe . take 1
. dropWhile ((<= x) . snd)
. sortBy (comparing snd)
. dropWhile ((/= x) . snd)
. zip [toEnum 0..]
此 Maybe
查找列表中位于给定元素上方 之后的下一个最小元素的索引元素,在输入列表中。按照您的要求。
您可以使用您选择的任何Enum
类型作为索引。
现在,您可以使用高效的Map
数据结构将您的已排序元素(到目前为止,x
之上)保留为直接递归,以直接递归实现来查找倒数第二个,等等。
正确性更高,效率更高!
效率更新:在排序后删除 会删除它们 sorted ,所以那里浪费了精力;实际上,应该在排序之前将其替换为过滤(如the answer中的Luis Morillo所示)。并且如果我们的元素类型在 测试:Integral
中(因此它是一个适当的离散类型,不像Enum
,这要归功于@dfeuer来pointing了!),这里有一个机会优化的更多机会:如果我们通过纯粹的机会击中succ
个最小元素 ,就没有进一步改善的机会,因此我们应该在那儿纾困:< / p>
bar :: (Integral a, Enum b) => a -> [a] -> Maybe b
bar x = fmap fst . either Just (listToMaybe . take 1
. sortBy (comparing snd))
. findOrFilter ((== succ x).snd) ((> x).snd)
. dropWhile ((/= x) . snd)
. zip [toEnum 0..]
findOrFilter :: (a -> Bool) -> (a -> Bool) -> [a] -> Either a [a]
findOrFilter t p = go
where go [] = Right []
go (x:xs) | t x = Left x
| otherwise = fmap ([x | p x] ++) $ go xs
> foo 5 [2,3,5,4,6,5,2,1,7,9,2] :: Maybe Int
Just 4
> foo 2 [2,3,5,4,6,5,2,1,7,9,2] :: Maybe Int
Just 1
> foo 1 [3,5,4,6,5,2,1,7,9,2] :: Maybe Int
Just 9
答案 3 :(得分:1)
我不清楚您到底想要什么。根据示例,我猜是:找到高于x的最小元素的索引,该元素出现在x 之后。在这种情况下,此解决方案是简单的Prelude
。没有进口
minList :: Ord a => a -> [a] -> Int
minList x l = snd . minimum . filter (\a -> x < fst a) . dropWhile (\a -> x /= fst a) $ zip l [0..]
逻辑是:
[(elem, index)]
创建zip l [0..]
对的列表x
找到输入dropWhile (\a -> x /= fst a)
x
丢弃小于filter (\a -> x < fst a)
的元素snd