[英]What is a good way to implement the equivalent of Haskell's “or” function using Streams in Java
[英]What is the Java equivalent of this Haskell function?
Philip Wadler 的论文“Monads for Functional Programming”有一个函数 eval 的例子,它执行用 Haskell 编写的除法。
这是 Graham Hutton 在“Programming in Haskell”中改编的:
data Expr = Val Int | Div Expr Expr
eval :: Expr -> Int
eval (Val n) = n
eval (Div x y) = eval x `div` eval y
我的 Java 相当于:
abstract class IntegerExpression {
abstract Integer evaluate();
}
class Value extends IntegerExpression {
Integer value;
public Value(Integer x) {
value = x;
}
public Integer evaluate() {
return value;
}
}
class DivisionExpression extends IntegerExpression {
IntegerExpression x, y;
public DivisionExpression(IntegerExpression a, IntegerExpression b) {
x = a;
y = b;
}
public Integer evaluate() {
return x.evaluate() / y.evaluate();
}
}
public class DivisionExample {
public static void main(String[] args) {
IntegerExpression two = new Value(2);
IntegerExpression twenty = new DivisionExpression (new Value(100), new Value(5));
IntegerExpression ten = new DivisionExpression(twenty, new Value(2));
IntegerExpression five = new DivisionExpression(new Value(10), two);
IntegerExpression expr = new DivisionExpression(ten, five);
System.out.println(expr.evaluate());
}
}
这看起来不错,但是如何开发此代码以便我可以在 Java 中演示 Try monad(以捕捉除以零)?
编辑:在这种情况下失败的解决方法是使用Maybe Monad
,他在 Java 中的堂兄是Optional
类,其中Option.of
将return
而flatMap
将是bind
。 另一方面,在 Java 和其他 OO 语言中,在这种称为复合的情况下,有一种通用模式可以使用,基本上你的数据类型Expr
将是一个接口或抽象类,而类型构造函数将是叶子:所以,用考虑到所有这些,一个简单的工作示例是:
在哈斯克尔:
data Expr = Val Int | Div Expr Expr
eval :: Expr -> Maybe Int
eval (Val n) = Just n
eval (Div x y) = do
v1 <- eval x
v2 <- eval y
if v2 == 0
then Nothing
else return (div v1 v2)
n1 = Val 8
n2 = Val 4
n3 = Val 0
d1 = Div n1 n2
d2 = Div d1 d1
d3 = Div d2 n3
main = do
putStrLn $ show (eval d2)
putStrLn $ show (eval d3)
Java中的等效方法:
import java.util.Optional;
public interface Expr {
public Optional<Integer> eval();
}
然后叶子实现 Expr:
import java.util.Optional;
public class Val implements Expr{
Optional<Integer> value;
public Val(int value) {
this.value = Optional.of(value);
}
@Override
public Optional<Integer> eval() {
return value;
}
}
然后递归的情况:
import java.util.Optional;
public class Div implements Expr {
Expr expr1;
Expr expr2;
public Div(Expr expr1, Expr expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public Optional<Integer> eval() {
return expr1.eval().flatMap(v1 ->
expr2.eval().flatMap(v2 ->
(v2 == 0) ? Optional.empty() : Optional.of(v1 / v2)
)
);
}
public static void main(String[] args) {
Expr iv1 = new Val(6);
Expr iv2 = new Val(3);
Expr iv3 = new Val(2);
Expr iv4 = new Val(0);
Expr div1 = new Div(iv1, iv2);
Expr div2 = new Div(div1, iv3);
Expr div3 = new Div(div2, iv4);
System.out.println(div2.eval());
System.out.println(div3.eval());
}
}
主要功能输出将是:
Optional[1]
Optional.empty
其他答案涵盖了在 Java 中实现这一点的更惯用的方法,并描述了如何使用Optional
来处理错误。 但在这里我想给出 Java 中 Haskell 模式匹配的直接等价物,使用访问者模式:
public class ExprTest {
public static void main(String[] arguments) {
// expr :: Expr
// expr = Div
// (Div
// (Div (Val 100) (Val 5))
// (Val 2))
// (Div (Val 10) (Val 2))
Expr two = new Val(2);
Expr twenty = new Div(new Val(100), new Val(5));
Expr ten = new Div(twenty, new Val(2));
Expr five = new Div(new Val(10), two);
Expr expr = new Div(ten, five);
// eval :: Expr -> Int
// eval expr = case expr of
ExprVisitor<Integer> eval = new ExprVisitor<Integer>() {
// Val value -> value
public Integer visit(Val val) {
return val.value;
}
// Div left right -> eval left `div` eval right
public Integer visit(Div div) {
return div.left.match(this) / div.right.match(this);
}
};
// main = print (eval expr)
System.out.println(expr.match(eval));
}
}
// data Expr
abstract class Expr {
abstract <T> T match(ExprVisitor<T> visitor);
}
// = Val Int
class Val extends Expr {
public final int value;
public Val(int value) {
this.value = value;
}
<T> T match(ExprVisitor<T> visitor) {
return visitor.visit(this);
}
}
// | Div Expr Expr
class Div extends Expr {
public final Expr left, right;
public Div(Expr left, Expr right) {
this.left = left;
this.right = right;
}
<T> T match(ExprVisitor<T> visitor) {
return visitor.visit(this);
}
}
abstract class ExprVisitor<T> {
abstract T visit(Val val);
abstract T visit(Div div);
}
在函数式编程领域,这称为 Böhm–Berarducci 编码——有时也称为 Church 编码,尽管它们是不同的东西。 这是“用函数表示数据类型和模式匹配”的一种听起来很花哨的方式。 您当然可以在 Haskell 中使用这种匹配编码:
match
:: (Int -> t) -- visit(Val)
-> (Expr -> Expr -> t) -- visit(Div)
-> Expr
-> t
match val div expr = case expr of
Val x -> val x
Div left right -> div left right
eval :: Expr -> Int
eval = match id (\ left right -> eval left `div` eval right)
由于eval
是递归的,您也可以使用定点组合器fix
编写它——然后在 Java 版本的ExprVisitor
中使用this
可能会变得更加清晰:这就是您如何使eval
递归!
import Data.Function (fix)
eval :: Expr -> Int
eval = fix $ \ this -> match
(\ value -> value)
(\ left right -> this left `div` this right)
这是编码的另一半:我们可以完全取消数据类型,只使用函数:
{-# LANGUAGE RankNTypes #-}
newtype Expr = Expr
{ visit
:: forall a.
(Int -> a) -- Val
-> (a -> a -> a) -- Div
-> a }
valE :: Int -> Expr
valE x = Expr $ \ v _d -> v x
divE :: Expr -> Expr -> Expr
divE left right = Expr $ \ v d
-> d (visit left v d) (visit right v d)
eval :: Expr -> Int
eval expr = visit expr
(\ val -> val)
(\ left right -> left `div` right)
eval (divE
(divE (divE (valE 100) (valE 5)) (valE 2))
(divE (valE 10) (valE 2)))
== 2
eval
实现当然可以写成这样:
eval = visit expr id div
您可以在除法之前评估x
和y
:
Integer xE = x.evaluate(), yE = y.evaluate();
然后查看yE
是否等于0
:
if(yE == 0){
// your logic here if it is a division by 0
}
这将为您提供以下功能:
public Integer evaluate() {
Integer xE = x.evaluate(), yE = y.evaluate();
if(yE == 0){
// your logic here if it is a division by 0
}
return xE / yE;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.