我有一个List并希望将其减少为单个值(函数式编程术语“fold”,Ruby术语inject
),如
Arrays.asList("a", "b", "c") ... fold ... "a,b,c"
由于我感染了函数式编程思想(Scala),我正在寻找一种比
更简单/更短的编码方式sb = new StringBuilder
for ... {
append ...
}
sb.toString
答案 0 :(得分:13)
回答你原来的问题:
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(','));
答案 1 :(得分:10)
给出
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(); }
}
);
答案 2 :(得分:8)
如果要将一些功能方面应用于普通旧Java,而不切换语言although you could LamdaJ,fork-join (166y)和google-collections是帮助您添加该语法的库糖。
在google-collections的帮助下,您可以使用Joiner class:
Joiner.on(",").join("a", "b", "c")
Joiner.on(",")
是一个不可变对象,因此您可以自由共享它(例如作为常量)。
您还可以配置空值处理,例如Joiner.on(", ").useForNull("nil");
或Joiner.on(", ").skipNulls()
。
为避免在生成大字符串时分配大字符串,可以使用它通过Appendable
接口或StringBuilder
类附加到现有的Streams,StringBuilders等:
Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");
在写出地图时,您需要两个不同的分隔符来表示键和值之间的分隔和分隔:
Joiner.on(", ").withKeyValueSeparator(":")
.join(ImmutableMap.of(
"today", "monday"
, "tomorrow", "tuesday"))
答案 3 :(得分:6)
你正在寻找的是一个字符串“join”函数,遗憾的是,Java没有。你必须滚动你自己的连接功能,这不应该太难。
编辑: org.apache.commons.lang.StringUtils似乎有许多有用的字符串函数(包括连接)。
答案 4 :(得分:5)
您正在寻找的是Java自8.0以来的字符串join()
方法。尝试以下方法之一。
静态方法String#join(delimiter, elements)
:
Collection<String> source = Arrays.asList("a", "b", "c");
String result = String.join(",", source);
Stream接口支持折叠操作,与Scala的foldLeft
函数非常相似。请查看以下连接Collector:
Collection<String> source = Arrays.asList("a", "b", "c");
String result = source.stream().collect(Collectors.joining(","));
您可能希望静态导入Collectors.joining
以使代码更清晰。
顺便说一下,这个收集器可以应用于任何特定对象的集合:
Collection<Integer> numbers = Arrays.asList(1, 2, 3);
String result = numbers.stream()
.map(Object::toString)
.collect(Collectors.joining(","));
答案 5 :(得分:2)
答案 6 :(得分:2)
首先,您需要一个Java功能库,它提供通用仿函数和折叠等功能投影。我在这里设计并实现了一个功能强大的(简单的)简单的库: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"
请注意,通过应用fold,唯一真正需要考虑的部分是Func2.call的实现,3行代码定义了一个接受累加器和元素并返回累加器的运算符(我的实现帐户为空字符串和空值,如果你删除那个案例,那么它就是2行代码。)
这是Seq.foldl的实际实现,Seq实现了Iterable&lt; E&gt;:
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;
}
答案 7 :(得分:2)
GS Collections有injectInto(比如Ruby),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);
注意:我是GS Collections的开发人员。
答案 8 :(得分:1)
不幸的是,Java不是一种函数式编程语言,并没有很好的方法来做你想做的事。
我相信Apache Commons lib有一个function called join可以做你想做的事。
在方法中隐藏循环必须足够好。
public static String combine(List<String> list, String separator){
StringBuilder ret = new StringBuilder();
for(int i = 0; i < list.size(); i++){
ret.append(list.get(i));
if(i != list.size() - 1)
ret.append(separator);
}
return ret.toString();
}
我想你可以递归地做到这一点:
public static String combine(List<String> list, String separator){
return recursiveCombine("", list, 0, separator);
}
public static String recursiveCombine(String firstPart, List<String> list, int posInList, String separator){
if (posInList == list.size() - 1) return firstPart + list.get(posInList);
return recursiveCombine(firstPart + list.get(posInList) + separator, list, posInList + 1, seperator);
}
答案 9 :(得分:1)
现在,您可以将String.join()
与Java 8一起使用。
List strings = Arrays.asList("a", "b", "c");
String joined = String.join(",", strings);
System.out.println(joined);
答案 10 :(得分:1)
在lambdas的支持下,我们可以使用以下代码:
man xcodebuild
答案 11 :(得分:1)
下面是折叠列表的代码,通过在我们前进的过程中保留留下的节点和折叠的信息。
public class FoldList {
public static void main(String[] args) {
Node a = new Node(1);
Node b = new Node(2);
Node c = new Node(3);
Node d = new Node(4);
Node e = new Node(5);
Node f = new Node(6);
Node g = new Node(7);
Node h = new Node(8);
Node i = new Node(9);
a.next = b;
b.next = c;
c.next = d;
d.next = e;
e.next = f;
f.next = g;
g.next = h;
h.next = i;
foldLinkedList(a);
}
private static void foldLinkedList(Node a) {
Node middle = getMiddleNodeOfTheList(a);
reverseListOnWards(middle);
foldTheList(a, middle);
}
private static Node foldTheList(Node a, Node middle) {
Node leftBackTracePtr = a;
Node leftForwardptr = null;
Node rightBackTrack = middle;
Node rightForwardptr = null;
Node leftCurrent = a;
Node rightCurrent = middle.next;
while (middle.next != null) {
leftForwardptr = leftCurrent.next;
rightForwardptr = rightCurrent.next;
leftBackTracePtr.next = rightCurrent;
rightCurrent.next = leftForwardptr;
rightBackTrack.next = rightForwardptr;
leftCurrent = leftForwardptr;
leftBackTracePtr = leftCurrent;
rightCurrent = middle.next;
}
leftForwardptr = leftForwardptr.next;
leftBackTracePtr.next = middle;
middle.next = leftForwardptr;
return a;
}
private static void reverseListOnWards(Node node) {
Node startNode = node.next;
Node current = node.next;
node.next = null;
Node previous = null;
Node next = node;
while (current != null) {
next = current.next;
current.next = previous;
previous = current;
current = next;
}
node.next = previous;
}
static Node getMiddleNodeOfTheList(Node a) {
Node slowptr = a;
Node fastPtr = a;
while (fastPtr != null) {
slowptr = slowptr.next;
fastPtr = fastPtr.next;
if (fastPtr != null) {
fastPtr = fastPtr.next;
}
}
return slowptr;
}
static class Node {
public Node next;
public int value;
public Node(int value) {
this.value = value;
}
}
}
答案 12 :(得分:0)
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
答案 13 :(得分:-3)
没有这样的功能,但您可以创建类似下面的内容,并在需要时调用它。
import java.util.Arrays;
import java.util.List;
public class FoldTest {
public static void main( String [] args ) {
List<String> list = Arrays.asList("a","b","c");
String s = fold( list, ",");
System.out.println( s );
}
private static String fold( List<String> l, String with ) {
StringBuilder sb = new StringBuilder();
for( String s: l ) {
sb.append( s );
sb.append( with );
}
return sb.deleteCharAt(sb.length() -1 ).toString();
}
}