我尝试为Java的class RoundedLabel: UILabel {
private lazy var maskLayer: CAShapeLayer = {
let maskLayer = CAShapeLayer()
maskLayer.cornerRadius = 5
maskLayer.masksToBounds = true
self.layer.mask = maskLayer
return maskLayer
}()
override func layoutSubviews() {
super.layoutSubviews()
let maskPath = UIBezierPath(roundedRect: bounds,
byRoundingCorners: [.topRight, .bottomRight, .bottomLeft],
cornerRadii: CGSize(width: 10, height: 10))
maskLayer.path = maskPath.cgPath
maskLayer.frame = bounds
}
}
构建等效的Javascript并达到
IntStream.range(0, 5).forEach(System.err::println);
Java的所有流魔术都内置在普通的JavaScript数组中。为什么Java中的const IntStream = (function () {
function range(start, end, numbers = []) {
if (start === end) {
return numbers
}
return range(start + 1, end, numbers.concat(start))
}
return {
range
}
})()
IntStream.range(0, 5).forEach(number => console.log(number))
不能像ArrayList
那样完成相同的事情,还是有一个我没想到的目的呢?
答案 0 :(得分:3)
数组高阶函数将在每一步都急切地完成整个过程。
const isOdd = v => v % 2 == 1;
const multiply = by => v => v * by;
const arrRange = IntStream.range(10, 20);
const arrOdd = arrRange.filter(isOdd);
const arrOddM3 = arrOdd.map(multiply(3));
这里所有绑定都是由每个步骤创建的不同数组。即使你链接它们,也总是制作中间数组,每一步的整个数组都需要在下一步开始之前完成。
const arrOddM3 = IntStream.range(10, 20).filter(isOdd).map(multiply(3));
arrOddM3; // ==> [33, 39, 45, 51, 57]
Streams是不同的,因为它们只在访问时计算值。流版本看起来非常相似。
const streamOddM3 = Stream.range(10, Infinity).filter(isOdd).map(multiply(3));
streamOddM3; // ==> Stream
注意我已将结束更改为无穷大。我可以这样做,因为它最多会计算第一个值,而某些实现根本不会进行任何计算,直到您要求这些值。要强制进行计算,您可以获取一些值并将它们作为数组返回:
streamOddM3.take(3); // ==> [33, 39, 45]
这是一个基于SICP videos松散的Stream实现,它的工作类似于Java的流。
class EmptyStream {
map() {
return this;
}
filter() {
return this;
}
take() {
return [];
}
}
class Stream extends EmptyStream {
constructor(value, next) {
super();
this._next = next;
this.value = value;
}
/**
* This prevents the value to be computed more than once
* @returns {EmptyStream|Stream}
*/
next() {
if( ! (this._next instanceof EmptyStream) ) {
this._next = this._next();
}
return this._next;
}
map(fn) {
return new Stream(fn(this.value), () => this.next().map(fn));
}
filter(fn) {
return fn(this.value) ?
new Stream(this.value, () => this.next().filter(fn)) :
this.next().filter(fn);
}
take(n) {
return n == 0 ? [] : [this.value, ...this.next().take(n && n - 1)];
}
static range(from, to, step = 1) {
if (to !== undefined && ( step > 0 && from > to || step < 0 && from < to )) {
return Stream.emptyStream;
}
return new Stream(from, () => Stream.range(from + step, to, step));
}
}
Stream.emptyStream = new EmptyStream();
在JavaScript中你有generators(又名协同程序),你可以制作一个地图和过滤器生成器函数,它接受一个生成器源并成为一个带有该转换的新生成器。由于它已经在语言中,它可能比Streams更好地匹配,但我还没有研究它足以制作上述的生成器示例。
在Clojure中,你有transducers,它允许你编写步骤,这样只有在使得它成为最终结果的元素时才会发生最终列表。它们很容易用JavaScript实现。
答案 1 :(得分:2)
Streams和Javasvript阵列之间存在很大差异:
[1,2,3,4]
.filter(el => {
console.log(el);
return el%2 === 0;
})
.forEach( el => console.log(el));
javascript中的结果将是:
1,2,3,4 2,4
对于Stream,它将是:
1,2 2 3,4 4
因此,您可以看到javascript改变了集合,然后迭代集合。传递给Stream的元素遍历流。如果将集合传递给Stream,则将在流中传递一个接一个的元素。
可能的Stream实现将是:
class Stream {
constructor(){
this.queue = [];
}
//the modifying methods
forEach(func){
this.queue.push(["forEach",func]);
return this;
}
filter(func){
this.queue.push(["filter",func]);
return this;
}
map(func){
this.queue.push(["map",func]);
return this;
}
subStream(v){
this.forEach(d => v.get(d));
return this;
}
//data methods
get(value,cb){
for( let [type,func] of this.queue ){
switch(type){
case "forEach":
func(value);
break;
case "map":
value = func(value);
break;
case "filter":
if(! func(value)) return;
}
}
cb(value);
}
range(start,end){
const result = [];
Array.from({length:end-start})
.forEach((_,i)=> this.get(i+start, r => result.push(r)));
return result;
}
}
USECASE:
const nums = new Stream();
const even = new Stream();
even.filter(n => !(n%2) ).forEach(n => console.log(n));
const odd = new Stream();
even.filter(n => (n%2) ).forEach(n => console.log(n));
nums
.subStream(even)
.subStream(odd)
.range(0,100);
答案 2 :(得分:1)
不是因为他们如何处理数据而不一样。 在LINQ(C#)或javascript中,集合上的每个操作必须在调用管道中的下一个操作时结束。
在溪流中,它与众不同。例如:
Arrays.asList(1,2,3).stream()
.filter((Integer x)-> x>1)
.map((Integer x)->x*10)
.forEach(System.out::println);
源集合:1,2,3
forEach(20) - &gt; print 30.源集合中没有更多元素。 完成对流的冲击。
输出: 20 30
插图:
这种方法的结果之一是有时管道中的某些操作不会覆盖每个元素,因为其中一些操作在过程中被过滤掉了。