java - 如何使用 Java 1.8 实现教会数字

标签 java generics lambda java-8 church-encoding

我正在尝试在 Java 1.8 中实现教会数字。我的第一次尝试是:

import java.util.function.UnaryOperator;

@FunctionalInterface
public interface ChurchNumeral {
  public static ChurchNumeral valueOf(int n) {
    if (n < 0) {
      throw new IllegalArgumentException("Argument n must be non-negative.");
    }
    if (n == 0) {
      return (f, arg) -> arg;
    }
    return (f, arg) -> f(valueOf(n-1).apply(f, arg));
  }

  <T> T apply(UnaryOperator<T> f, T arg);
}

这失败了,因为功能方法有一个类型参数。 (具体来说,带有 lambda 表达式的行给出错误:“非法的 lambda 表达式:ChurchNumeral 类型的方法应用是通用的”。)

根据对函数式接口(interface)使用泛型的相关问题的回答,我尝试对类进行参数化:

import java.util.function.UnaryOperator;

@FunctionalInterface
public interface ChurchNumeral<T> {                // This line changed.
  public static ChurchNumeral<?> valueOf(int n) {  // This line changed.
    if (n < 0) {
      throw new IllegalArgumentException("Argument n must be non-negative.");
    }
    if (n == 0) {
      return (f, arg) -> arg;
    }
    return (f, arg) -> f(valueOf(n-1).apply(f, arg));
  }

  T apply(UnaryOperator<T> f, T arg);              // This line changed.
}

第一个 lambda 表达式现在可以编译,但第二个会失败并出现此错误:

The method apply(UnaryOperator, capture#1-of ?) in the type ChurchNumeral is not applicable for the arguments (UnaryOperator, Object)

此外,我不想为每种可能的函数/参数类型使用不同版本的 ChurchNumeral.ZERO。

有什么建议吗?

最佳答案

static interface Church<T> extends UnaryOperator<UnaryOperator<T>> {

    static <T> Church<T> of(int n) {
        if (n < 0) {
            throw new IllegalArgumentException();
        } else if (n == 0) {
            return f -> (t -> t);
        } else {
            return sum(f -> f, Church.of(n-1));
        }
    }

    static <T> Church<T> sum(Church<T> a, Church<T> b) {
        return f -> b.apply(f).andThen(a.apply(f))::apply;
    }
}

public static void main(String[] args) {
    Church<Integer> five = Church.of(5);
    Church<Integer> three = Church.of(3);
    Church<Integer> eight = Church.sum(five, three);

    assert 3 == three.apply(x -> x + 1).apply(0);
    assert 5 == five.apply(x -> x + 1).apply(0);
    assert 8 == eight.apply(x -> x + 1).apply(0);
}

编辑如果你想要apply(UnaryOperator<T> f, T arg)就在教堂界面,这样你就可以调用.apply(x->x+1,0)而不是 .apply(x->x+1).apply(0) ,你可以在上面的 Church 接口(interface)中添加一个这样的默认方法:

default T apply(UnaryOperator<T> f, T t) {
    return this.apply(f).apply(t);
}

编辑 2: 这是可以在不同类型之间进行转换的更新类。我还添加了一个 mul乘法只是为了看看它是如何工作的:

static interface Church<T> extends UnaryOperator<UnaryOperator<T>> {

    static <T> Church<T> of(int n) {
        if (n < 0) {
            throw new IllegalArgumentException();
        } else if (n == 0) {
            return zero();
        } else {
            return sum(one(), Church.of(n - 1));
        }
    }

    static <T> Church<T> zero() {
        return f -> (t -> t);
    }

    static <T> Church<T> one() {
        return f -> f;
    }

    static <T> Church<T> sum(Church<T> a, Church<T> b) {
        return f -> b.apply(f).andThen(a.apply(f))::apply;
    }

    static <T> Church<T> mul(Church<T> a, Church<T> b) {
        return f -> a.apply(b.apply(f))::apply;
    }

    default <U> Church<U> convert() {
        return (Church<U>) this;
    }
}

public static void main(String[] args) {
    Church<Integer> zero = Church.zero();
    Church<Integer> five = Church.of(5);
    Church<Integer> three = Church.of(3);
    Church<Integer> eight = Church.sum(five, three);
    Church<Integer> fifteen = Church.mul(three, five);

    assert 0 == zero.apply(x -> x + 1).apply(0);
    assert 3 == three.apply(x -> x + 1).apply(0);
    assert 5 == five.apply(x -> x + 1).apply(0);
    assert 8 == eight.apply(x -> x + 1).apply(0);
    assert 15 == fifteen.apply(x -> x + 1).apply(0);

    Church<String> strOne = Church.one();     
    Church<String> strThree = three.convert();  // make Church<String>
                                                // from a Church<Integer>

    assert "foo:bar".equals(strOne.apply("foo:"::concat).apply("bar"));
    assert "foo:foo:foo:bar".equals(strThree.apply("foo:"::concat).apply("bar"));

}

关于java - 如何使用 Java 1.8 实现教会数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29042938/

相关文章:

java - Android AsycTask运行时间计算

java - 搜索自定义链表 - 哪种方法更好?

java - 有没有一种方法可以通用地测量物体的大小或长度?

java - 流和 Lambda 表达式按降序排序

javascript - Hogan 在预编译模式下不支持 lambda

node.js - 为 NodeJS Lambda 函数重用 Redis 连接

java - 使用 Okhttp3 的 OPTIONS/HEAD REST API 请求

java - Android 中未知的 NullPointerException

java - Generic 方法中的参数如何同时分配给 Integer 和 Character 类?

java - 在Java中调用方法Design