我有一个列表,并且想要将其缩减为单个值(函数式编程术语“折叠”,Ruby术语inject
),例如
Arrays.asList("a", "b", "c") ... fold ... "a,b,c"
由于我受到函数式编程思想(Scala)的影响,我正在寻找一种比...更简单/更短的编码方式。
sb = new StringBuilder
for ... {
append ...
}
sb.toString
我有一个列表,并且想要将其缩减为单个值(函数式编程术语“折叠”,Ruby术语inject
),例如
Arrays.asList("a", "b", "c") ... fold ... "a,b,c"
由于我受到函数式编程思想(Scala)的影响,我正在寻找一种比...更简单/更短的编码方式。
sb = new StringBuilder
for ... {
append ...
}
sb.toString
回答你的原问题:
public static <A, B> A fold(F<A, F<B, A>> f, A z, Iterable<B> xs)
{ A p = z;
for (B x : xs)
p = f.f(p).f(x);
return p; }
F 的长相如下:
public interface F<A, B> { public B f(A a); }
正如dfa所建议的那样,Functional Java已经实现了这个功能,并且还有更多其他功能。
示例1:
import fj.F;
import static fj.data.List.list;
import static fj.pre.Monoid.stringMonoid;
import static fj.Function.flip;
import static fj.Function.compose;
F<String, F<String, String>> sum = stringMonoid.sum();
String abc = list("a", "b", "c").foldLeft1(compose(sum, flip(sum).f(",")));
例子2:
import static fj.data.List.list;
import static fj.pre.Monoid.stringMonoid;
...
String abc = stringMonoid.join(list("a", "b", "c"), ",");
示例3:
import static fj.data.Stream.fromString;
import static fj.data.Stream.asString;
...
String abc = asString(fromString("abc").intersperse(','));
考虑到
public static <T,Y> Y fold(Collection<? extends T> list, Injector<T,Y> filter){
for (T item : list){
filter.accept(item);
}
return filter.getResult();
}
public interface Injector<T,Y>{
public void accept(T item);
public Y getResult();
}
fold(myArray, new Injector<String,String>(){
private StringBuilder sb = new StringBuilder();
public void Accept(String item){ sb.append(item); }
public String getResult() { return sb.toString(); }
}
);
yield
更多地是一个函数式编程术语,而且由于 fold
也是一个函数式编程术语,所以使用 yield
应该更好。 - EskoCollection<? extends T> list
而不是 super
,因为列表是 T 的生产者。 - Juraj BlahunkaJoiner.on(",").join("a", "b", "c")
Joiner.on(",")
是一个不可变对象,因此您可以自由地共享它(例如作为常量)。Joiner.on(", ").useForNull("nil");
或Joiner.on(", ").skipNulls()
。Appendable
接口或StringBuilder
类将其附加到现有的流、字符串构建器等中:Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");
Joiner.on(", ").withKeyValueSeparator(":")
.join(ImmutableMap.of(
"today", "monday"
, "tomorrow", "tuesday"))
join()
方法,Java 从8.0版本开始支持。请尝试以下方法之一。
Static method String#join(delimiter, elements)
:
Collection<String> source = Arrays.asList("a", "b", "c");
String result = String.join(",", source);
Stream interface supports a fold operation very similar to Scala’s foldLeft
function. Take a look at the following concatenating Collector:
Collection<String> source = Arrays.asList("a", "b", "c");
String result = source.stream().collect(Collectors.joining(","));
You may want to statically import Collectors.joining
to make your code clearer.
By the way this collector can be applied to collections of any particular objects:
Collection<Integer> numbers = Arrays.asList(1, 2, 3);
String result = numbers.stream()
.map(Object::toString)
.collect(Collectors.joining(","));
Eclipse Collections具有类似于Ruby和Smalltalk中的“injectInto”、 “makeString”和“appendString”方法。以下内容可适用于您的示例:
String result1 = FastList.newListWith("a", "b", "c").makeString(",");
StringBuilder sb = new StringBuilder();
FastList.newListWith("a", "b", "c").appendString(sb, ",");
String result2 = sb.toString();
Assert.assertEquals("a,b,c", result1);
Assert.assertEquals(result1, result2);
Java 8的风格(函数式):
// Given
List<String> arr = Arrays.asList("a", "b", "c");
String first = arr.get(0);
arr = arr.subList(1, arr.size());
String folded = arr.stream()
.reduce(first, (a, b) -> a + "," + b);
System.out.println(folded); //a,b,c
首先,您需要一个为Java提供通用functor和功能投影(例如fold)的功能库。我在这里设计并实现了一个功能强大(通过优势)但简单的库:http://www.codeproject.com/KB/java/FunctionalJava.aspx(我发现其他提到的库过于复杂)。
然后,您的解决方案将如下所示:
Seq.of("","a",null,"b","",null,"c","").foldl(
new StringBuilder(), //seed accumulator
new Func2<StringBuilder,String,StringBuilder>(){
public StringBuilder call(StringBuilder acc,String elmt) {
if(acc.length() == 0) return acc.append(elmt); //do not prepend "," to beginning
else if(elmt == null || elmt.equals("")) return acc; //skip empty elements
else return acc.append(",").append(elmt);
}
}
).toString(); //"a,b,c"
public <R> R foldl(R seed, final Func2<? super R,? super E,? extends R> binop)
{
if(binop == null)
throw new NullPointerException("binop is null");
if(this == EMPTY)
return seed;
for(E item : this)
seed = binop.call(seed, item);
return seed;
}
很不幸,在Java中你无法避免这个循环,但是有几个库可以使用。例如,你可以尝试以下几个库:
static <T, R> R foldL(BiFunction<R, T, R> lambda, R zero, List<T> theList){
if(theList.size() == 0){
return zero;
}
R nextZero = lambda.apply(zero,theList.get(0));
return foldL(lambda, nextZero, theList.subList(1, theList.size()));
}