如何在Java中实现列表折叠

如何在Java中实现列表折叠,java,collections,functional-programming,folding,Java,Collections,Functional Programming,Folding,我有一个列表,希望将其简化为一个值(函数式编程术语“fold”,Ruby术语inject),比如 由于我深受函数式编程思想(Scala)的影响,我正在寻找一种比函数式编程更简单/更短的编码方法 sb = new StringBuilder for ... { append ... } sb.toString 不幸的是,在Java中,您无法逃脱这个循环,但是有几个库。例如,您可以尝试几个库: 特别是在你的情况下,你可以重复使用我的 您需要的是一个字符串“join”函数,不幸的是,Jav

我有一个列表,希望将其简化为一个值(函数式编程术语“fold”,Ruby术语
inject
),比如

由于我深受函数式编程思想(Scala)的影响,我正在寻找一种比函数式编程更简单/更短的编码方法

sb = new StringBuilder
for ... {
  append ...
}
sb.toString

不幸的是,在Java中,您无法逃脱这个循环,但是有几个库。例如,您可以尝试几个库:

  • 特别是在你的情况下,你可以重复使用我的

您需要的是一个字符串“join”函数,不幸的是,Java没有这个函数。您将不得不使用自己的连接函数,这应该不会太难


Edit:似乎有许多有用的字符串函数(包括join)。

不幸的是,Java不是函数式编程语言,没有一种很好的方法来实现您想要的功能

我相信ApacheCommons库有一个可以实现您想要的功能

它必须足够好,才能在方法中隐藏循环

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();
}
公共静态字符串组合(列表、字符串分隔符){
StringBuilder ret=新的StringBuilder();
对于(int i=0;i
我想你可以递归地做:

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);
}
公共静态字符串组合(列表、字符串分隔符){
返回递归组合(“,列表,0,分隔符);
}
公共静态字符串recursiveCombine(字符串第一部分、列表、int-posInList、字符串分隔符){
if(posInList==list.size()-1)返回firstPart+list.get(posInList);
返回递归组合(firstPart+list.get(posInList)+分隔符,list,posInList+1,分隔符);
}
给定

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();
}

公共静态Y折叠(集合回答您的原始问题:

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; }
例3:

import static fj.data.Stream.fromString;
import static fj.data.Stream.asString;
...
String abc = asString(fromString("abc").intersperse(','));

如果您希望在不切换语言的情况下将一些功能方面应用于普通的旧Java,并且这些库可以帮助您添加语法糖

在的帮助下,您可以使用:

Joiner.on(“,”)
是一个不可变的对象,因此您可以自由地共享它(例如作为常量)

您还可以配置空处理,如
Joiner.on(“,”).useForNull(“nil”);
Joiner.on(“,”).skipNulls()

为了避免在生成大字符串时分配大字符串,可以使用它通过
appendeable
接口或
StringBuilder
类附加到现有流、StringBuilder等:

Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");
在编写映射时,需要两个不同的分隔符来分隔条目和键+值:

Joiner.on(", ").withKeyValueSeparator(":")
            .join(ImmutableMap.of(
            "today", "monday"
            , "tomorrow", "tuesday"))

首先,您需要一个Java函数库,它提供通用的函子和函数投影,比如fold。我在这里设计并实现了一个功能强大(凭借其优点)但简单的函数库:(我发现前面提到的其他库过于复杂)

然后,您的解决方案将如下所示:

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"
(“,”a“,”空“,”b“,”空“,”c“,”)的顺序。foldl( 新建StringBuilder(),//种子累加器 新功能2(){ 公共StringBuilder调用(StringBuilder acc、String elmt){ 如果(acc.length()==0),则返回acc.append(elmt);//不在“,”之前加上前缀 else if(elmt==null | | elmt.equals(“”))返回acc;//跳过空元素 否则返回acc.append(“,”).append(elmt); } } ).toString();/“a、b、c”
注意,通过应用fold,真正需要考虑的唯一部分是Func2.call的实现,3行代码定义了一个接受累加器和一个元素并返回累加器的操作符(我的实现考虑了空字符串和空值,如果去掉这种情况,那么只需要2行代码)

下面是Seq.foldl的实际实现,Seq实现了Iterable:


public R foldl(R seed,final Func2没有这样的函数,但是您可以创建如下内容,并在需要时调用它

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();

    }
}
导入java.util.array;
导入java.util.List;
公共类民俗{
公共静态void main(字符串[]args){
List=Arrays.asList(“a”、“b”、“c”);
字符串s=折叠(列表“,”);
系统输出打印项次;
}
私有静态字符串折叠(列表l,带的字符串){
StringBuilder sb=新的StringBuilder();
for(字符串s:l){
某人追加;
给某人加上;
}
返回sb.deleteCharAt(sb.length()-1.toString();
}
}
注入
(如Ruby和Smalltalk)、
生成字符串
追加字符串
。以下内容适用于您的示例:

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);

注意:我是Eclipse集合的提交者。

您正在寻找的是Java自8.0以来一直使用的string
join()
方法。请尝试以下方法之一

  • 静态方法:

    您可能希望静态导入
    收集器。加入
    以使代码更清晰

    顺便说一下,此收集器可以应用于任何特定对象的集合:

    Collection<Integer> numbers = Arrays.asList(1, 2, 3);
    String result = numbers.stream()
            .map(Object::toString)
            .collect(Collectors.joining(","));
    
    collectionnumber=Arrays.asList(1,2,3);
    字符串结果=numbers.stream()
    .map(对象::toString)
    .collect(收集器。连接(“,”);
    

  • 现在,您可以在Java8中使用
    String.join()

    List strings=Arrays.asList(“a”、“b”、“c”);
    连接的字符串=字符串。连接(“,”,字符串);
    System.out.println(已连接);
    
    在lambdas的支持下,我们可以使用以下代码:

    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()));                  
        }
    
    static R foldL(双功能lambda,R zero,列表){
    如果(列表大小()==0){
    返回零;
    }
    R nextZero=lambda.apply(零,theList.get(0));
    返回foldL(lambda,nextZero,theList.subList(1,theList.size());
    }
    
    下面是通过保留节点信息来折叠列表的代码
    Joiner.on(", ").withKeyValueSeparator(":")
                .join(ImmutableMap.of(
                "today", "monday"
                , "tomorrow", "tuesday"))
    
    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;
    }
    
    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();
    
        }
    }
    
    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);
    
    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = source.stream().collect(Collectors.joining(","));
    
    Collection<Integer> numbers = Arrays.asList(1, 2, 3);
    String result = numbers.stream()
            .map(Object::toString)
            .collect(Collectors.joining(","));
    
    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()));                  
        }
    
    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;
            }
    
        }
    }
    
    // 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