我在Haskell中有这个函数,我想知道如何将它转换为Java,特别是使用流:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
答案 0 :(得分:6)
(我不是Haskell专家,但我知道这很危险。)
给出的示例代码有几个合理映射的Haskell结构 以及Java构造:
Haskell列表很懒,所以它对应于Java Stream。
使用的范围是整数,因此它们对应于IntStream。
例如,[240..1280]
对应IntStream.rangeClosed(240, 1280)
。
带有步骤的范围在Java中没有直接对应关系,但它很容易
计算;你只需要做一些算术然后映射值
从顺序范围到步骤范围。例如,[2, 4..20]
可以写成
IntStream.rangeClosed(1, 10).map(i -> 2 * i)
列表推导的条件对应于过滤流 谓词。
使用多个生成器的理解对应于flatmapping 嵌套流。
在Java中没有通用的方法来表示元组。各种第三方 库提供了有关的各种权衡的元组实现 仿制药和拳击。或者,您可以使用字段编写自己的类 你要。 (如果你使用很多不同类型的话,这可能会非常繁琐 但是,元组。)在这种情况下,元组只是四个整数,所以很容易 使用带有四个元素的int数组表示。
总而言之,我们得到以下结论。
static Stream<int[]> build() {
return IntStream.rangeClosed(240, 1280).boxed()
.flatMap(w -> IntStream.rangeClosed(1, 10).map(m -> 2 * m).boxed()
.flatMap(m -> IntStream.rangeClosed(2, 100).boxed()
.flatMap(n -> IntStream.rangeClosed(240, 1280)
.filter(g -> ((w - 2*m - n*g) % (n+1) == 0))
.filter(g -> n*g+2*m <= w)
.filter(g -> n*g <= w)
.mapToObj(g -> new int[] { w, m, n, g }))));
}
与原始的Haskell相比,这显然非常冗长,但您可以很容易地看到Haskell结构在Java代码中的最终位置。我相信这是正确的,因为它似乎生成与Haskell代码相同的输出。
请注意,我们使用IntStream
生成值,但我们希望flatmap提供数组流(对象),而IntStream.flatMap
返回IntStream
。也许理想情况下会有flatMapToObj
操作,但没有,所以我们必须将int值插入Integer
对象,然后调用它Stream.flatMap
。
可以将流管道分配给Stream类型的变量,但这不是很方便,因为Java流最多可以使用一次。由于构造这样的流很便宜(与评估它相比),编写一个函数build()
是合理的,该函数返回一个新创建的流,准备由调用者进行评估。
运行以下Java代码时,
System.out.println(build().count());
System.out.println(build().findFirst().map(Arrays::toString).orElse("not found"));
System.out.println(build().reduce((a, b) -> b).map(Arrays::toString).orElse("not found"));
结果是:
654559
[484, 2, 2, 240]
[1280, 20, 5, 248]
运行以下Haskell代码(build
的定义从问题中复制)
build = [(w,m,n,g) | w <- [240..1280], m <- [2,4..20], n <- [2..100], g <- [240..1280],
((w - 2*m - n*g) `mod` (n+1) == 0), n*g+2*m <= w, n*g <= w]
main = do
print (length build)
print (head build)
print (last build)
给出以下输出:
654559
(484,2,2,240)
(1280,20,5,248)
因此,我的眼睛看起来是正确的音译。
head
(Java,findFirst
)和last
(Java,reduce((a, b) -> b)
)操作的时间如下:(使用GHC 7.6.3更新 - O2)
head last
GHC 8s 36s
JDK 3s 9s
这至少表明两个系统都提供了懒惰,因为计算在找到第一个元素后被短路,而找到最后一个元素则需要计算所有元素。
有趣的是,在Haskell中,调用length
,head
和last
中的所有三个不会花费更多时间而不仅仅是调用last
(大约36秒)因为记忆。 Java中没有任何memoization,但当然你可以将结果显式存储在一个数组或List中并多次处理。
总的来说,我对Java实现的速度有多惊讶。我并不真正理解Haskell的性能,所以我将把它留给Haskell专家来评论。我很可能做错了,虽然大多数情况下我只是将问题中的函数复制到文件中并使用GHC进行编译。
我的环境:
JDK 9,GHC 7.6.3 -O2,MacBook Pro 2014年中期2核3GHz英特尔酷睿i7