如何使用 Java 1.8 实现教会数字
How to implement Church Numerals using Java 1.8
我正在尝试在 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 表达式的行给出错误:"Illegal lambda expression: Method apply of type ChurchNumeral is generic"。)
根据对函数式接口使用泛型相关问题的回答,我尝试参数化 class:
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)
此外,我不想为每种可能的 function/argument 类型设置不同版本的 ChurchNumeral.ZERO。
有什么建议吗?
Is there a way to do it so I don't need to create a ChurchNumeral for
every possible type? I'd like to be able to apply ZERO (for example)
to any UnaryOperator and argument of type T
我假设你的意思是你想做这样的事情:
ChurchNumeral five = ChurchNumeral.valueOf(5);
five.apply(s -> s + s, "s");
five.apply(Math::sqrt, Double.MAX_VALUE);
这意味着您第一个示例中的方法签名:
<T> T apply(UnaryOperator<T> f, T arg);
就是需要的那个。
然而,you can't use a lambda expression for a functional interface, if the method in the functional interface has type parameters.
解决方法是创建一个与 lambda 兼容的子接口,并将对 apply
的调用委托给它的方法,如下所示。
public static void main(String[]a){
ChurchNumeral five = ChurchNumeral.valueOf(5);
System.out.println(five.apply(s -> s + s, "s"));
System.out.println(five.apply(Math::sqrt, Double.MAX_VALUE));
}
@FunctionalInterface
private interface ChurchNumeralT<T> extends ChurchNumeral {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
default<U> U apply(UnaryOperator<U> f, U arg){
return (U)((ChurchNumeralT)this).tapply(f, arg);
}
T tapply(UnaryOperator<T> f, T arg);
}
public interface ChurchNumeral {
<T> T apply(UnaryOperator<T> f, T arg);
static ChurchNumeral valueOf(int n) {
if (n < 0) {
throw new IllegalArgumentException("Argument n must be non-negative.");
}
if (n == 0) {
return (ChurchNumeralT<?>)(f, arg) -> arg;
}
return (ChurchNumeralT<?>)(f, arg) -> f.apply(valueOf(n - 1).apply(f, arg));
}
}
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 界面:
default T apply(UnaryOperator<T> f, T t) {
return this.apply(f).apply(t);
}
编辑 2: 这是更新后的 class,可以在不同类型之间进行转换。我还添加了一个 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 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 表达式的行给出错误:"Illegal lambda expression: Method apply of type ChurchNumeral is generic"。)
根据对函数式接口使用泛型相关问题的回答,我尝试参数化 class:
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)
此外,我不想为每种可能的 function/argument 类型设置不同版本的 ChurchNumeral.ZERO。
有什么建议吗?
Is there a way to do it so I don't need to create a ChurchNumeral for every possible type? I'd like to be able to apply ZERO (for example) to any UnaryOperator and argument of type T
我假设你的意思是你想做这样的事情:
ChurchNumeral five = ChurchNumeral.valueOf(5);
five.apply(s -> s + s, "s");
five.apply(Math::sqrt, Double.MAX_VALUE);
这意味着您第一个示例中的方法签名:
<T> T apply(UnaryOperator<T> f, T arg);
就是需要的那个。
然而,you can't use a lambda expression for a functional interface, if the method in the functional interface has type parameters.
解决方法是创建一个与 lambda 兼容的子接口,并将对 apply
的调用委托给它的方法,如下所示。
public static void main(String[]a){
ChurchNumeral five = ChurchNumeral.valueOf(5);
System.out.println(five.apply(s -> s + s, "s"));
System.out.println(five.apply(Math::sqrt, Double.MAX_VALUE));
}
@FunctionalInterface
private interface ChurchNumeralT<T> extends ChurchNumeral {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
default<U> U apply(UnaryOperator<U> f, U arg){
return (U)((ChurchNumeralT)this).tapply(f, arg);
}
T tapply(UnaryOperator<T> f, T arg);
}
public interface ChurchNumeral {
<T> T apply(UnaryOperator<T> f, T arg);
static ChurchNumeral valueOf(int n) {
if (n < 0) {
throw new IllegalArgumentException("Argument n must be non-negative.");
}
if (n == 0) {
return (ChurchNumeralT<?>)(f, arg) -> arg;
}
return (ChurchNumeralT<?>)(f, arg) -> f.apply(valueOf(n - 1).apply(f, arg));
}
}
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 界面:
default T apply(UnaryOperator<T> f, T t) {
return this.apply(f).apply(t);
}
编辑 2: 这是更新后的 class,可以在不同类型之间进行转换。我还添加了一个 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"));
}