Question

J'ai une liste et que vous souhaitez le réduire à une valeur unique (terme de programmation fonctionnelle « fold », Ruby terme inject), comme

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

Comme je suis infecté avec des idées de programmation fonctionnelle (Scala), je suis à la recherche d'un moyen plus facile / plus court que ce codage

sb = new StringBuilder
for ... {
  append ...
}
sb.toString
Était-ce utile?

La solution

Ce que vous cherchez est une méthode de join() de chaîne qui a depuis Java 8.0. Essayez l'une des méthodes suivantes.

  1. Méthode statique String#join(delimiter, elements) :

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
  2. streaming interface prend en charge une opération de pliage très similaire à la fonction de foldLeft Scala. Jetez un oeil à l'adresse suivante concaténer Collector :

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

    Vous pouvez importer statiquement Collectors.joining pour rendre votre code plus clair.

    Par la façon dont ce collecteur peut être appliqué aux collections de tous les objets particuliers:

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

Autres conseils

Pour répondre à votre question initiale:

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

Où F ressemble à ceci:

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

DFA a suggéré, Java fonctionnelle a cette mise en œuvre, et plus encore.

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

Exemple 2:

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

Exemple 3:

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

Étant donné

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

Ensuite, l'utilisation ressemble

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 vous voulez appliquer certains aspects fonctionnels au simple vieux Java, sans changer de langue bien que vous pourriez href="http://code.google.com/p/lambdaj/" rel="nofollow noreferrer"> LamdaJ , fourche join (166y) et google-collections sont les bibliothèques qui vous aident à ajouter que le sucre syntaxique.

Avec l'aide de google-collections vous pouvez utiliser le class Joiner :

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

Joiner.on(",") est un objet immuable de sorte que vous pouvez partager librement (par exemple comme une constante).

Vous pouvez également configurer la manipulation comme nulle Joiner.on(", ").useForNull("nil"); ou Joiner.on(", ").skipNulls().

Pour éviter d'allouer des grandes chaînes pendant que vous générez une grande chaîne, vous pouvez l'utiliser pour ajouter à cours d'eau existants, StringBuilders, etc., par la classe d'interface Appendable ou StringBuilder:

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

Lors de l'écriture des cartes, vous avez besoin de deux séparateurs différents pour les entrées et entre seperation valeur de +:

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

Ce que vous cherchez est une fonction chaîne « join » qui, malheureusement, Java n'a pas. Vous devrez rouler votre propre fonction join qui ne devrait pas être trop dur.

Modifier org.apache.commons.lang.StringUtils semble avoir de nombreuses fonctions de chaîne utiles (y compris rejoindre).

malheureusement en Java vous ne pouvez pas échapper à cette boucle, il y a plusieurs bibliothèques cependant. Par exemple. vous pouvez essayer plusieurs bibliothèques:

D'abord, vous aurez besoin d'une bibliothèque fonctionnelle pour Java qui fournit foncteurs génériques et des projections fonctionnelles comme pli. Je l'ai conçu et mis en place un puissant (en vertu) mais simple telle bibliothèque ici: http: //www.codeproject.com/KB/java/FunctionalJava.aspx (j'ai trouvé les autres bibliothèques mentionnées trop compliqué).

Ensuite, votre solution ressemblerait à ceci:

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"

Notez qu'en appliquant fois, la seule partie qui a vraiment besoin d'être pensé est la mise en œuvre de Func2.call, 3 lignes de code qui définissent l'opérateur accepte l'accumulateur et un élément et retourner l'accumulateur (ma mise en œuvre pour les comptes chaînes vides et nulls, si vous supprimez ce cas, il est jusqu'à 2 lignes de code).

Et voici la mise en œuvre effective de Seq.foldl, Seq implémente 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 Collections a injectInto (comme Ruby), makeString et appendString. Ce qui suit travaillera avec votre exemple:

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

Note:. Je suis un développeur sur GS Collections

Malheureusement, Java n'est pas un langage de programmation fonctionnelle et n'a pas une bonne façon de faire ce que vous voulez.

Je crois que la lib Apache Commons a fonction appelée joindre qui va faire ce que vous voulez bien.

Il devra être assez bon pour cacher la boucle dans une méthode.

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

Je suppose que vous pouvez le faire de manière récursive:

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

Maintenant, vous pouvez utiliser String.join() avec Java 8.

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

Avec le soutien de lambdas que nous pourrions faire avec le code suivant:

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

Voici le code à plier la liste, en gardant en retour l'information des noeuds laisser derrière et plier alors que nous avançons.

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 style (fonctionnel):

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

Il n'y a pas une telle fonction, mais vous pouvez créer quelque chose comme ce qui suit, et l'appeler à chaque fois que vous avez besoin.

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

    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top