Domanda

Ho un Elenco di ridurlo a un singolo valore (programmazione funzionale termine di "piegare", Ruby termine inject), come

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

Come io sono infettati con la programmazione funzionale idee (Scala), sto cercando una più facile/più breve modo per codice di

sb = new StringBuilder
for ... {
  append ...
}
sb.toString
È stato utile?

Soluzione

Quello che state cercando è un metodo join() stringa che Java ha dato 8.0. Prova uno dei metodi indicati.

  1. Metodo statico String#join(delimiter, elements) :

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
  2. streaming interfaccia supporta un'operazione di piega molto simile alla funzione foldLeft di Scala. Date un'occhiata alla Collector :

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

    Si consiglia di importare staticamente Collectors.joining per rendere il codice più chiaro.

    A proposito questo collettore può essere applicato a insiemi di oggetti particolari:

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

Altri suggerimenti

Per rispondere alla tua domanda iniziale:

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

Dove F è simile al seguente:

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

Come suggerito DFA, Java funzionale ha implementato questo, e altro ancora.

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

Esempio 2:

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

Esempio 3:

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

Dato

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

E poi l'utilizzo che sembra proprio

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

Se si desidera applicare alcuni aspetti funzionali a plain old Java, senza cambiare la lingua anche se si potrebbe href="http://code.google.com/p/lambdaj/" rel="nofollow noreferrer"> LamdaJ , fork-join (166y) e google-collezioni sono librerie che consentono di aggiungere che lo zucchero sintattico.

Con l'aiuto di google-collezioni è possibile utilizzare il Joiner classe :

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

Joiner.on(",") è un oggetto immutabile per cui si potrebbe condividere liberamente (ad esempio come una costante).

È anche possibile configurare la movimentazione nulla come Joiner.on(", ").useForNull("nil"); o Joiner.on(", ").skipNulls().

Per evitare di allocare grandi corde mentre si sta generando una stringa di grandi dimensioni, è possibile utilizzarlo per aggiungere ai flussi esistenti, StringBuilders, ecc attraverso la classe di interfaccia Appendable o StringBuilder:

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

Quando si scrive le mappe, avete bisogno di due separatori diversi per le voci e la separazione tra il valore + chiave:

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

Quello che state cercando è una funzione stringa "join" che, purtroppo, Java non ha. Si dovrà rotolare il proprio unirsi funzione che non dovrebbe essere troppo difficile.

Modifica org.apache.commons.lang.StringUtils sembra avere molte funzioni di stringa utile (compresi i join).

purtroppo in Java non si può sfuggire a quel ciclo, ci sono diverse librerie comunque. Per esempio. si può provare diverse librerie:

In primo luogo è necessario una libreria funzionale per Java che fornisce funtori generici e proiezioni funzionali come piega. Ho progettato e implementato un potente (in virtù) ma semplice come biblioteca qui: http: //www.codeproject.com/KB/java/FunctionalJava.aspx (ho trovato le altre librerie menzionati troppo complicato).

Quindi la soluzione sarebbe simile:

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"

Si noti che, applicando piega, l'unica parte che ha davvero bisogno di essere pensato è l'implementazione per Func2.call, 3 linee di codice che definiscono un operatore accettare l'accumulatore e un elemento e restituendo l'accumulatore (conti la mia implementazione per stringhe e valori nulli vuote, se si rimuove questo caso poi si è scesi a 2 righe di codice).

Ed ecco l'effettiva attuazione del Seq.foldl, Seq 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 Collezioni ha injectInto (come Ruby), makeString e appendString. Di seguito lavorerà con il tuo esempio:

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:. Sono uno sviluppatore su GS Collezioni

Purtroppo Java non è un linguaggio di programmazione funzionale, e non ha un buon modo per fare quello che vuoi.

Credo che la lib Apache Commons ha una funzione chiamata unirsi che farà ciò che si vuole però.

Si dovrà essere abbastanza buono per nascondere l'anello in un metodo.

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

Immagino che si potrebbe fare in modo ricorsivo:

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

Ora è possibile utilizzare String.join() con Java 8.

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

Con il supporto di lambda che potremmo fare con il seguente codice:

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

Di seguito è riportato il codice per piegare l'elenco, tenendo indietro l'informazione dei nodi lasciare alle spalle e pieghevole come andare avanti.

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 stile (funzionale):

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

Non c'è una funzione tale, ma è possibile creare qualcosa di simile a quanto segue, e richiamare ogni volta che è necessario.

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

    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top