Philip Wadler的论文“用于函数式编程的Monad”中有一个函数eval的示例,该函数执行用Haskell编写的除法。
此处是Graham Hutton撰写的“在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(捕捉零除)?
答案 0 :(得分:6)
编辑:在这种情况下解决故障的方法是使用Maybe Monad
,而他在Java中的堂兄是Optional
类,其中Option.of
将是{{1 }}和return
为flatMap
。另一方面,在Java和其他程序中在这种情况下,有一种通用模式称为复合模式,基本上,您的数据类型bind
将是接口或抽象类,而类型构造函数将是叶子:
因此,考虑到所有这些,一个简单的示例工作将是:
Expr
然后执行Expr的叶子:
import java.util.Optional;
public interface Expr {
public Optional<Integer> eval();
}
然后是递归情况:
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());
}
}
答案 1 :(得分:2)
您可以在除法之前同时评估use Magento\Catalog\Model\Product\Type as ProductType;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableType;
use Magento\GroupedProduct\Model\Product\Type\Grouped as GroupedType;
switch ($product->getTypeId()) {
case ProductType::TYPE_SIMPLE:
// ....
break;
case ProductType::TYPE_BUNDLE:
// ....
break;
case ProductType::TYPE_VIRTUAL:
// ....
break;
case ConfigurableType::TYPE_CODE:
// ....
break;
case GroupedType::TYPE_CODE:
// ....
break;
}
和x
:
y
然后查看Integer xE = x.evaluate(), yE = y.evaluate();
是否等于yE
:
0
将为您带来以下功能:
if(yE == 0){
// your logic here if it is a division by 0
}
答案 2 :(得分:1)
其他答案涵盖了在Java中实现此操作的更惯用的方法,并描述了如何使用Optional
处理错误。但是在这里,我想用 visitor模式:
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
来编写它,然后在{{1 }}在Java版本中可能会变得更加清晰:这就是使this
递归的方式!
ExprVisitor
这是编码的另一半:我们可以完全取消数据类型,而只使用函数:
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