Data.Map 函数作为 Haskell 中的值

Data.Map with functions as values in Haskell

我是一个 Haskell 新手,试图了解如何使用 Data.Map 结构、惰性求值和 Maybe 类型。

在Python中我可以定义一个字典结构,其值为函数。给定一个键我就可以使用相应的函数:

d = {"+": lambda x,y: x+y}

def f(key, v1, v2):
    if key in d:
        return d[key](v1, v2)
    else:
        return 0

我曾尝试在 Haskell 中做类似的事情,但它无法编译。

d = Map.fromList [('+', (+))]

f :: Char -> Integer -> Integer -> Integer
f key v1 v2 =
    if k == Nothing
        then 0
        else f v1 v2
    where
        k = Map.lookup key d
        (Just f) = k

这不会编译并且 returns 像

这样的错误
No instance for (Eq (Integer -> Integer -> Integer))

我相信这是因为 Map.lookup '+' d 只是 returns Maybe (Integer -> Integer -> Integer) 的一个实例,而不是 (Just (+))Nothing。我认为这与惰性评估有关。

有没有类似Haskell的方法来做这种事情?我是否错误地使用了 Maybe 类型?我可以强制对查找进行评估吗?

这是因为我试图在 Haskell 中实现反向抛光计算器。我用字典来组织我可能使用的功能。我找到了一个很好的解决方案 (https://rosettacode.org/wiki/Parsing/RPN_calculator_algorithm#Haskell),但现在我只想了解如何正确访问 Haskell Data.Map 中的值。

确实,函数 (== Nothing) 作为类型 (Eq a) => Maybe a -> Bool,在你的例子中,函数不是 Eq 类型类的成员,这不会编译。

但是您可以使用 Data.Maybe 中的 isNothing 函数,或者您可以像这样自己定义它:

isNothing :: Maybe a -> Bool
isNothing Nothing = True
isNothing (Just _) = False

因为您在构造函数上进行模式匹配,因此不使用 (==),所以您不需要 a 成为 Eq.

的实例

问题是这个表达式:k == Nothing

需要k支持相等性测试。 k 的类型是 Maybe (Integer -> Integer -> Integer)。如果 T 支持,Maybe T 支持相等性测试,但 Integer -> Integer -> Integer 不支持:您无法比较函数的相等性。所以整个表达式没有类型检查。

我不知道你所说的“Maybe (Integer -> Integer -> Integer) 的实例”是什么意思:类 有实例(类型),但是Maybe (Integer -> Integer -> Integer)不是class,是普通类型。有问题的 class 是 Eq (它提供了 == 方法)。问题是函数类型没有 Eq 实例。这也与惰性求值无关。


解决方案是只使用模式匹配:

f key v1 v2 =
    case Map.lookup key d of
        Nothing -> 0
        Just f  -> f v1 v2
        -- but consider naming 'f' something else;
        -- the surrounding function is already called 'f'

或者,您可以使用 Maybe 辅助函数之一:

f key v1 v2 =
    maybe 0 (\f -> f v1 v2) (Map.lookup key d)

甚至:

f key v1 v2 =
    fromMaybe (\_ _ -> 0) (Map.lookup key d) v1 v2

这是因为您在比较表达式中使用了类型为 Maybe (Integer -> Integer -> Integer)k。 Haskell 不知道如何比较两个函数。

一个解决方案是'unpack'查找的return。

import qualified Data.Map as Map                                                                 

d = Map.fromList [('+', (+))]                                                                    

f :: Char -> Integer -> Integer -> Integer                                                       
f key v1 v2 =                                                                                    
  case Map.lookup key d of                                                                       
    Nothing -> 0                                                                                 
    Just f -> f v1 v2

.

λ> f '-' 1 2                                                                                     
0                                                                                                
λ> f '+' 1 2                                                                                     
3                                                                                                
λ>