此Haskell函数的Java等效项是什么?

时间:2018-06-21 11:24:24

标签: java haskell

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(捕捉零除)?

3 个答案:

答案 0 :(得分:6)

编辑:在这种情况下解决故障的方法是使用Maybe Monad,而他在Java中的堂兄是Optional类,其中Option.of将是{{1 }}和returnflatMap。另一方面,在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模式

直接给出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来编写它,然后在{{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