How to implement a list fold in Java
Given
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();
}
Then usage just looks like
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(); }
}
);
If you want to apply some functional aspects to plain old Java, without switching language although you could LamdaJ, fork-join (166y) and google-collections are libraries that help you to add that syntactic sugar.
With the help of google-collections you can use the Joiner class:
Joiner.on(",").join("a", "b", "c")
Joiner.on(",")
is an immutable object so you might share it freely (for example as a constant).
You can also configure null handling like Joiner.on(", ").useForNull("nil");
or Joiner.on(", ").skipNulls()
.
To avoid allocating big strings while you are generating a large string, you can use it to append to existing Streams, StringBuilders, etc. through the Appendable
interface or StringBuilder
class:
Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");
When writing out maps, you need two different separators for entries and seperation between key+value:
Joiner.on(", ").withKeyValueSeparator(":")
.join(ImmutableMap.of(
"today", "monday"
, "tomorrow", "tuesday"))
What you are looking for is a string join()
method which Java has since 8.0. Try one of the methods below.
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(","));
To answer your original question:
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; }
Where F looks like this:
public interface F<A, B> { public B f(A a); }
As dfa suggested, Functional Java has this implemented, and more.
Example 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(",")));
Example 2:
import static fj.data.List.list;
import static fj.pre.Monoid.stringMonoid;
...
String abc = stringMonoid.join(list("a", "b", "c"), ",");
Example 3:
import static fj.data.Stream.fromString;
import static fj.data.Stream.asString;
...
String abc = asString(fromString("abc").intersperse(','));