我想识别哪个函数作为参数传递给高阶函数。 我怎样才能做到这一点?使用模式匹配? 我想做类似下面的代码:
add x y = x+y
sub x y = x-y
myFunc :: (a->a->a) -> a -> a -> IO a
myFunc add x y = do print "add was performed"
add x y
myFunc sub x y = do print "sub was performed"
sum x y
myFunc f x y = do print "another function was performed"
f x y
如果无法做到这一点,有没有人有其他想法呢?
答案 0 :(得分:10)
不,这是不可能的。
你可以通过拥有代表操作的数据类型来实现这种效果,也许
data Operation
= Add (a -> a -> a)
| Sub (a -> a -> a)
| Other (a -> a -> a)
myFunc :: Operation -> a -> a -> IO a
myFunc (Add f) x y = do print "add was performed"
return (f x y)
myFunc (Sub f) x y = do print "sub was performed"
return (f x y)
myFunc (Other f) x y = do print "another function was performed"
return (f x y)
答案 1 :(得分:3)
不可能完全按照您的要求行事。我建议您改为使用嵌入式域特定语言(EDSL)并为其编写一个或多个解释器。最常见的方法是使用代数数据类型或(在更复杂的情况下)使用广义代数数据类型来表示EDSL。在这里你可能会有像
这样的东西data Expr a = Lit a
| BinOp (Op a) (Expr a) (Expr a)
deriving (Show)
data Op a = Add
| Sub
| Other (a -> a -> a)
instance Show (Op a) where
show Add = "Add"
show Sub = "Sub"
show Other{} = "Other"
现在,您可以编写一个带Expr a
并执行请求操作的求值程序:
evalExpr :: Num a => Expr a -> a
evalExpr (Lit x) = x
evalExpr (BinOp op e1 e2) = runOp op (evalExpr e1) (evalExpr e2)
runOp :: Num a => Op a -> a -> a -> a
runOp Add a b = a + b
runOp Sub a b = a - b
runOp (Other f) a b = f a b
您也可以添加跟踪:
evalExpr' :: (Num a, MonadWriter [(Expr a, a)] m) => Expr a -> m a
evalExpr' e = do
result <- case e of
Lit a -> return a
BinOp op e1 e2 -> runOp op <$> evalExpr' e1 <*> evalExpr' e2
tell [(e, result)]
return result
样品使用:
*Write> runWriter $ evalExpr' (BinOp Add (Lit 3) (BinOp Sub (Lit 4) (Lit 5)))
(2,[(Lit 3,3),(Lit 4,4),(Lit 5,5),(BinOp Sub (Lit 4) (Lit 5),-1),(BinOp Add (Lit 3) (BinOp Sub (Lit 4) (Lit 5)),2)])
为方便起见,您可以写
instance Num a => Num (Expr a) where
fromInteger = Lit . fromInteger
(+) = BinOp Add
(-) = BinOp Sub
然后上面可以缩写
*Write Control.Monad.Writer> runWriter $ evalExpr' (3 + (4-5))
(2,[(Lit 3,3),(Lit 4,4),(Lit 5,5),(BinOp Sub (Lit 4) (Lit 5),-1),(BinOp Add (Lit 3) (BinOp Sub (Lit 4) (Lit 5)),2)])
答案 2 :(得分:1)
也许是为了简化并且不要改变代码的整体外观,如果它已经是一个很长的项目并且这是一个问题,你可以做类似的事情:
add x y = x+y
sub x y = x-y
myFunc :: (Eq a, Num a) => (a->a->a) -> a -> a -> IO a
myFunc f x y = if (add x y) == (f x y) then
do print "add was performed"
return (add x y)
else if (sub x y) == (f x y) then
do print "sub was performed"
return (sub x y)
else
do print "another function was performed"
return (f x y)
它的工作原理,唯一的问题是你不能区分例如乘法2 1中的加2 1,所以如果有可能你可以在那里抛出新的案例来覆盖所有重要的理由,比如只比较添加xy = fxy,也比较添加yx和fy x。有些人认为它会100%有效。