Pregunta

Tengo una lista y quiero reducirlo a un solo valor (término de programación funcional "pliegue", Ruby inject plazo), como

Arrays.asList("a", "b", "c") ... fold ... "a,b,c"

Como estoy infectado con ideas de programación funcionales (Scala), estoy buscando una manera más fácil / más corto para codificarlo que

sb = new StringBuilder
for ... {
  append ...
}
sb.toString
¿Fue útil?

Solución

Lo que se busca es un método join() cadena que Java tiene desde 8,0. Pruebe uno de los siguientes métodos.

  1. Método estático String#join(delimiter, elements) :

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
  2. Stream interfaz es compatible con una operación de plegado muy similar a la función foldLeft de Scala. Echar un vistazo a la siguiente concatenación Colector :

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = source.stream().collect(Collectors.joining(","));
    

    Es posible que desee importar estáticamente Collectors.joining para hacer su código más claro.

    Por la forma en que este colector se puede aplicar a las colecciones de objetos particulares:

    Collection<Integer> numbers = Arrays.asList(1, 2, 3);
    String result = numbers.stream()
            .map(Object::toString)
            .collect(Collectors.joining(","));
    

Otros consejos

Para responder a su pregunta original:

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

Donde F es el siguiente:

public interface F<A, B> { public B f(A a); }

Como sugirió DFA, de Java funcional ha implementado este, y mucho más.

Ejemplo 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(",")));

Ejemplo 2:

import static fj.data.List.list;
import static fj.pre.Monoid.stringMonoid;
...
String abc = stringMonoid.join(list("a", "b", "c"), ",");

Ejemplo 3:

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

Dado

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

A continuación, el uso sólo se parece a

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

Si desea aplicar algunos aspectos funcionales a viejo y simple de Java, sin tener que cambiar el lenguaje aunque se puede href="http://code.google.com/p/lambdaj/" rel="nofollow noreferrer"> LamdaJ , tenedor-join (166y) y Google-colecciones son las bibliotecas que le ayudan a añadir que el azúcar sintáctica.

Con la ayuda de Google-colecciones puede utilizar el Joiner clase :

Joiner.on(",").join("a", "b", "c")

Joiner.on(",") es un objeto inmutable por lo que podría compartirla libremente (por ejemplo, como una constante).

También puede configurar el manejo nula como Joiner.on(", ").useForNull("nil"); o Joiner.on(", ").skipNulls().

Para evitar la asignación de grandes cadenas, mientras que se está generando una gran cadena, puede utilizarlo para anexar a los flujos existentes, StringBuilders, etc a través de la clase de interfaz Appendable o StringBuilder:

Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");

Al escribir los mapas, se necesitan dos separadores diferentes para las entradas y la separación entre el valor llave +:

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

Lo que se busca es una función de cadena "unirse" a la que, por desgracia, Java no tiene. Usted tendrá que rodar su propia función join que no debe ser demasiado duro.

Editar: org.apache.commons.lang.StringUtils parece tener muchas funciones de cadena útiles (incluyendo unirse).

lamentablemente en Java no se puede escapar de ese bucle, hay varias bibliotecas sin embargo. P.ej. puede probar varias bibliotecas:

En primer lugar tendrás una biblioteca funcional para Java que suministra funtores genéricos y proyecciones funcionales como pliegue. He diseñado e implementado un poderoso (en virtud) pero sencillo como biblioteca aquí: http: //www.codeproject.com/KB/java/FunctionalJava.aspx (I pareció que las otras bibliotecas mencionan excesivamente complicado).

A continuación, la solución se vería así:

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"

Tenga en cuenta que mediante la aplicación de veces, la única parte que realmente necesita ser pensado es la implementación de Func2.call, 3 líneas de código que definen un operador que acepte el acumulador y un elemento y devolver el acumulador (cuentas de mi aplicación para cadenas vacías y nulos, si se quita ese caso, entonces es hasta 2 líneas de código).

Y aquí está la implementación real de Seq.foldl, Sec implementa Iterable :

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

GS Colecciones tiene injectInto (como Ruby), makeString y appendString. El siguiente trabajo con su ejemplo:

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

Nota:. Soy un desarrollador de GS Colecciones

Desafortunadamente Java no es un lenguaje de programación funcional y no tiene una buena manera de hacer lo que quiera.

Creo que el lib Apache Commons tiene un función llamada unirse que va a hacer lo que quiere sin embargo.

Se tendrá que ser lo suficientemente bueno como para ocultar el bucle en un método.

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

supongo que podría hacerlo de forma recursiva:

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

Ahora puede utilizar String.join() con Java 8.

    List strings = Arrays.asList("a", "b", "c");
    String joined = String.join(",", strings);
    System.out.println(joined);

Con el apoyo de lambdas que podríamos hacer con el siguiente código:

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

A continuación se muestra el código de plegar la lista, manteniendo la espalda la información de los nodos de dejar atrás y doblando a medida que avanzamos.

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

    }
}

Java 8 estilo (funcional):

// 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

No existe tal función, pero que podría crear algo así como lo siguiente, e invocarlo siempre que lo necesite.

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

    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top