2009-06-04 18 views
22

Tengo una lista y quiero reducirlo a un solo valor (término de programación funcional "pliegue", Ruby término inject), al igual queCómo implementar un pliegue lista en Java

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 de

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

¿Por qué no se escribe un método de ayuda y a llamar a eso? Puedes escribir tus propias funciones. –

+1

Claro, podría escribir uno usando el código proporcionado. Pero solo cuando estoy seguro de que no existe tal función. –

+0

Programación funcional en Java? Pisa con suavidad y usa botas gruesas. – Juliet

Respuesta

5

Lo que está buscando es un método de cadena join() que Java tiene desde 8.0. Pruebe uno de los métodos a continuación.

  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 de Scala foldLeft. Echar un vistazo a la siguiente concatenación Collector:

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

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

+2

Google Collections tiene una Interfaz de función y método Lists.map, pero no equivalente de fold. Sin embargo, tiene una clase Joiner que se adapta a esta aplicación en particular. –

6

Lo que se busca es una cadena "unen" función que, desafortunadamente, Java no tiene. Deberá desplegar su propia función de unión, que no debería ser demasiado difícil.

Editar:org.apache.commons.lang.StringUtils parece tener muchas funciones de cadena útiles (incluida la unión).

+0

Sí. Solo espero que nadie en su sano juicio elija el uso en http://stackoverflow.com/questions/950751/how-to-implement-a-list-fold-in-java/951004#951004 durante una llamada a StringUtils.join(), si el problema es * unir un grupo de cadenas en una * en Java. :) – Jonik

+4

@Jonik: Espero que nadie en su sano juicio esté listo para introducir una nueva dependencia solo por un solo método :) – Esko

+4

Sí, pero, una vez más, un proyecto típico se beneficiaría de usar * muchas cosas * de bibliotecas como Commons Lang o Guava. :) – Jonik

1

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

Creo que el Apache Commons lib tiene un function called join que hará lo que quieras sin embargo.

Tendrá que ser lo suficientemente bueno para ocultar el ciclo 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); 
} 
+0

Hombre, tantos downvotes hoy. Desearía saber por qué. – jjnguy

+0

Probablemente la solución recursiva (no te voté, por cierto). Esa es una gran manera de obtener un desbordamiento de pila. –

+0

Es desafortunado que Java no maneje bien este tipo de cosas, porque la solución recursiva a este tipo de cosas suele ser la más fácil de entender. –

10

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

Todavía no puedo decidir si prefiero llamar a Injector # getResult Injector # yield. –

+0

En mi opinión 'yield' es más un término de programación funcional y dado que' fold' también es un término de programación funcional, 'yield' debería ser mejor de usar. – Esko

+0

el parámetro de lista debe ser 'Collection list', no' super', porque list es un productor de Ts –

13

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 se parece a esto:

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

Como sugirió DFA, Functional Java tiene esta implementada, y 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(',')); 
8

Si desea aplicar algunos aspectos funcionales a viejo y simple de Java, sin tener que cambiar el lenguaje although you couldLamdaJ, fork-join (166y) y google-collections son l Bibliotecas que te ayudan a agregar ese azúcar sintáctico.

Con la ayuda de google-collections se puede utilizar el Joiner class:

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 nulo como Joiner.on(", ").useForNull("nil"); o Joiner.on(", ").skipNulls().

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

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

Al escribir 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")) 
2

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 una potente (por virtud) pero simple biblioteca como esta aquí: http://www.codeproject.com/KB/java/FunctionalJava.aspx (encontré las otras bibliotecas mencionadas demasiado complicadas).

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 aceptando el acumulador y un elemento y devolviendo el acumulador (mis cuentas de implementación para cadenas vacías y nulos, si elimina ese caso, se reduce a 2 líneas de código).

Y aquí es la aplicación real de Seq.foldl, Sec implementa Iterable <E>:

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

No hay tal función, pero se podía crear algo como lo siguiente, e invocar siempre que necesite a.

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

    } 
} 
+0

La llamada 'deleteCharAt' realmente me duele. Además, solo está eliminando el último carácter, sin importar cuánto dure 'con'. En realidad, sería mejor no agregar 'con 'en absoluto al final. Además, cuando la lista y 'con 'estén ambos vacíos, se lanzará una excepción. – Madoc

2

GS Collections tiene injectInto (como Ruby), makeString y appendString. Lo siguiente funcionará 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 Collections.

1

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

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

Con el apoyo de lambdas 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()));     
    } 
1

A continuación se muestra el código para doblar la lista, manteniendo oculta la información de los nodos retenidos y plegados 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; 
     } 

    } 
} 
0

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 
Cuestiones relacionadas