1から100までの足し算は、テンポラリーの変数を使ってこんなふうにかける。
sum = 0
(1..100).each {|n| sum += n}
puts "sum = #{sum}"
(1..100).each {|n| sum += n}
puts "sum = #{sum}"
Enumerable#reduceを使うと、こんなふうにかける。
sum = (1..100).reduce(0) {|memo, n| memo + n}
puts "sum = #{sum}"
puts "sum = #{sum}"
1回目のループでは、memoにはreduce(0)で指定した0が入る。nには1が入っている。
memo + nの結果は次のループではmemoに格納される。
つまり2回目のループではmemo=1, n=2で、3回目にはmemo=3, n=3、4回目にはmemo=6, n=4…… といった具合。
reduceのかわりにinjectでもよい。
単純な足し算だけじゃつまらないので、下記のようなUserというクラスがあったとして、
class User
attr_accessor :name, :age
def initialize(options={})
@name = options[:name]
@age = options[:age]
end
end
attr_accessor :name, :age
def initialize(options={})
@name = options[:name]
@age = options[:age]
end
end
Userインスタンスの配列の中から21歳以上のUserのnameだけを取り出したい場合には下記のようにすればよい。
users = [User.new(name: 'john', age: 20), User.new(name: 'hanako', age: 21),User.new(name: 'taro', age: 22)]
names = users.reduce([]) {|memo, u| u.age >= 21 ? memo << u.name : memo}
p names
# => ["hanako", "taro"]
names = users.reduce([]) {|memo, u| u.age >= 21 ? memo << u.name : memo}
p names
# => ["hanako", "taro"]
JavaではJava8でinjectが使える。
class User {
private final String name;
private final int age;
private User(final String nameArg, final int ageArg) {
name = nameArg;
age = ageArg;
}
static User newInstance(String name, int age) {
return new User(name, age);
}
String getName() {
return name;
}
int getAge() {
return age;
}
}
List list = new ArrayList<>();
list.add(User.newInstance("john", 20));
list.add(User.newInstance("hanako", 21));
list.add(User.newInstance("taro", 22));
List result = list.stream().reduce(new ArrayList(),
new BiFunction
private final String name;
private final int age;
private User(final String nameArg, final int ageArg) {
name = nameArg;
age = ageArg;
}
static User newInstance(String name, int age) {
return new User(name, age);
}
String getName() {
return name;
}
int getAge() {
return age;
}
}
List
list.add(User.newInstance("john", 20));
list.add(User.newInstance("hanako", 21));
list.add(User.newInstance("taro", 22));
List
new BiFunction
- , User, List
@Override
public List
if (u.getAge() >= 21) {
t.add(u.getName());
}
return t;
}
},
new BinaryOperator
- >() {
@Override
public List
t.addAll(u);
return t;
}
}
);
長い……
クラス定義やlistを作っているところはしようがないとしても、reduceのところが長い……
ラムダ式を使うと
result = list.stream().reduce(new ArrayList(), (memo, user) -> {
if (user.getAge() >= 21) {
memo.add(user.getName());
}
return memo;
}, (t, u) -> {
t.addAll(u);
return t;
});
if (user.getAge() >= 21) {
memo.add(user.getName());
}
return memo;
}, (t, u) -> {
t.addAll(u);
return t;
});
第3引数の存在がなぞ。
この例だと{}の中をthrow new NotImplementedException();と書いても動作する。
要素数が多くなったりすると活躍するのかなあ?
けれど、結局
result = new ArrayList<>();
for (User u : list) {
if (u.getAge() >= 21) {
result.add(u.getName());
}
}
のほうがわかりやすい?
for (User u : list) {
if (u.getAge() >= 21) {
result.add(u.getName());
}
}
0 件のコメント:
コメントを投稿