重构 Haskell 中的高阶函数以避免通过多个函数传递运算符
Refactor Higher Order Functions in Haskell to avoid passing Operators Through Several Functions
我正在尝试重构这段代码,以避免必须通过多个函数向下传递相同的运算符。
我正在编写的程序依赖于在顶层传入多个运算符,以便在较低层重用代码。我使用的模式的一个简化示例是:
add' = higherOrder (+)
subtract' = higherOrder (-)
higherOrder operator a b c d = d + someLowerFunction operator a b c
someLowerFunction operator a b c = c + someEvenLowerFunction a b operator
someEvenLowerFunction a b operator = operator a b
其中要调用的有用函数是 add'
和 subtract'
以及 higherOrder
、someLowerFunction
,而 someEvenLowerFunction
仅用于删除两个公开函数之间共有的代码。
有没有什么方法可以保留此代码的优点(即 higherOrder
、someLowerFunction
和 someEvenLowerFunction
可以重复使用)而不必通过 operator
一遍又一遍地?我试图通过代数数据类型来实现这一点,但到目前为止还没有取得好的结果。
我不完全理解你的问题,但我猜你想要类似的东西。
定义运算符的类型,给你想要的名称。
data Operator = Add | Sub
然后您可以定义它们的语义:
semOp :: Operator -> (Integer -> Integer -> Integer)
semOp Add = (+)
semOp Sub = (-)
然后,为算术表达式定义一个类型:
data Expr = Constant Integer | BinOp Expr Operator Expr
以及表达式的语义:
semExpr :: Expr -> Integer
semExpr (Constant a) = a
semExpr (BinOp e1 op e2) = semOp op (semExpr e1) (semExpr e2)
通过这种方式,二元运算符的语法和语义完全从表达式的定义及其语义中分离出来。
回顾这个问题,它本质上是一个关于将行为向下移动到更接近差异发生的地方的问题。从@chi 上面的回答中得到很大的启发(谢谢@chi!),我认为这是获得预期效果的最小改变:
data Operator = Add | Sub
add' = higherOrder Add
subtract' = higherOrder Sub
higherOrder :: Operator -> Int -> Int -> Int -> Int -> Int
higherOrder operator a b c d = d + someLowerFunction operator a b c
someLowerFunction :: Operator -> Int -> Int -> Int -> Int
someLowerFunction operator a b c = c + someEvenLowerFunction a b operator
someEvenLowerFunction :: Int -> Int -> Operator -> Int
someEvenLowerFunction a b Add = a + b
someEvenLowerFunction a b Sub = a - b
值得注意的是,我仍然向下传递运算符,但是在需要传递多个运算符的情况下,它们都可以只替换为允许稍后匹配的代数数据类型。
作为更笼统的评论,让代码这样缠绕在一起有点难闻。除了稍后的小差异外,有这么多共享的事实表明应该有一些更高的泛化可以用来完全解决这个问题。
我正在尝试重构这段代码,以避免必须通过多个函数向下传递相同的运算符。
我正在编写的程序依赖于在顶层传入多个运算符,以便在较低层重用代码。我使用的模式的一个简化示例是:
add' = higherOrder (+)
subtract' = higherOrder (-)
higherOrder operator a b c d = d + someLowerFunction operator a b c
someLowerFunction operator a b c = c + someEvenLowerFunction a b operator
someEvenLowerFunction a b operator = operator a b
其中要调用的有用函数是 add'
和 subtract'
以及 higherOrder
、someLowerFunction
,而 someEvenLowerFunction
仅用于删除两个公开函数之间共有的代码。
有没有什么方法可以保留此代码的优点(即 higherOrder
、someLowerFunction
和 someEvenLowerFunction
可以重复使用)而不必通过 operator
一遍又一遍地?我试图通过代数数据类型来实现这一点,但到目前为止还没有取得好的结果。
我不完全理解你的问题,但我猜你想要类似的东西。
定义运算符的类型,给你想要的名称。
data Operator = Add | Sub
然后您可以定义它们的语义:
semOp :: Operator -> (Integer -> Integer -> Integer)
semOp Add = (+)
semOp Sub = (-)
然后,为算术表达式定义一个类型:
data Expr = Constant Integer | BinOp Expr Operator Expr
以及表达式的语义:
semExpr :: Expr -> Integer
semExpr (Constant a) = a
semExpr (BinOp e1 op e2) = semOp op (semExpr e1) (semExpr e2)
通过这种方式,二元运算符的语法和语义完全从表达式的定义及其语义中分离出来。
回顾这个问题,它本质上是一个关于将行为向下移动到更接近差异发生的地方的问题。从@chi 上面的回答中得到很大的启发(谢谢@chi!),我认为这是获得预期效果的最小改变:
data Operator = Add | Sub
add' = higherOrder Add
subtract' = higherOrder Sub
higherOrder :: Operator -> Int -> Int -> Int -> Int -> Int
higherOrder operator a b c d = d + someLowerFunction operator a b c
someLowerFunction :: Operator -> Int -> Int -> Int -> Int
someLowerFunction operator a b c = c + someEvenLowerFunction a b operator
someEvenLowerFunction :: Int -> Int -> Operator -> Int
someEvenLowerFunction a b Add = a + b
someEvenLowerFunction a b Sub = a - b
值得注意的是,我仍然向下传递运算符,但是在需要传递多个运算符的情况下,它们都可以只替换为允许稍后匹配的代数数据类型。
作为更笼统的评论,让代码这样缠绕在一起有点难闻。除了稍后的小差异外,有这么多共享的事实表明应该有一些更高的泛化可以用来完全解决这个问题。