Pergunta

Eu tenho uma lista e quer reduzi-lo a um único valor (funcional termo de programação "dobrar", Ruby inject prazo), como

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

Como estou infectado com ideias de programação funcional (Scala), eu estou procurando uma maneira mais fácil / mais curto para o código-lo do que

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

Solução

O que você está procurando é um método join() corda que Java tem desde 8.0. Tente um dos métodos abaixo.

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

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
  2. Fluxo interface suporta uma operação de dobragem muito semelhante à função foldLeft Scala. Dê uma olhada no seguinte concatenação Collector :

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

    Você pode querer Collectors.joining estaticamente importação para tornar seu código mais claro.

    Pela forma como este coletor pode ser aplicado a coleções de quaisquer objetos particulares:

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

Outras dicas

Para responder à sua pergunta 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; }

Onde F parece com isso:

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

Como dfa sugeriu, Funcional Java tem esta implementada, e muito mais.

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

Exemplo 2:

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

Exemplo 3:

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

Dada

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

Uso Então apenas se parece com

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 você quiser aplicar alguns aspectos funcionais a planície antiga Java, sem mudar a linguagem embora você poderia href="http://code.google.com/p/lambdaj/" rel="nofollow noreferrer"> LamdaJ , fork-join (166y) e google-coleções são bibliotecas que ajudará a acrescentar que o açúcar sintático.

Com a ajuda de google-coleções você pode usar o Joiner classe :

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

Joiner.on(",") é um objeto imutável de modo que você pode compartilhá-la livremente (por exemplo, como uma constante).

Você também pode manuseio nula configure como Joiner.on(", ").useForNull("nil"); ou Joiner.on(", ").skipNulls().

Para evitar a alocação de grandes cadeias, enquanto você está gerando uma grande cadeia, você pode usá-lo para anexar a Streams existentes, StringBuilders, etc. através da classe de interface Appendable ou StringBuilder:

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

Ao escrever mapas, você precisa de dois separadores diferentes para entradas e separação entre o valor de chave +:

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

O que você está procurando é uma string "join" função que, infelizmente, Java não tem. Você vai ter que rolar o seu próprio juntar função que não deve ser muito difícil.

Editar: org.apache.commons.lang.StringUtils parece ter muitas funções de cadeia úteis (incluindo juntar).

infelizmente, em Java você não pode escapar que loop, existem várias bibliotecas no entanto. Por exemplo. você pode tentar várias bibliotecas:

Primeiro você vai precisar de uma biblioteca funcional para Java que fornece functors genéricos e projeções funcionais como vezes. Eu projetado e implementado um poderoso (em virtude), mas simples, tais biblioteca aqui: http: //www.codeproject.com/KB/java/FunctionalJava.aspx (eu encontrei as outras bibliotecas mencionadas excessivamente complicado).

Em seguida, a solução seria parecido com:

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"

Note que, ao aplicar vezes, a única parte que realmente precisa ser pensado é a implementação de Func2.call, 3 linhas de código que definem um operador que aceite acumulador e um elemento e retornando o acumulador (minha implementação é responsável por esvaziar cordas e nulos, se você remover o caso, então é até 2 linhas de código).

E aqui está a implementação real do Seq.foldl, implementos Seq 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 coleções tem injectInto (como Ruby), makeString e appendString. A seguir irá trabalhar com o seu exemplo:

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:. Eu sou um desenvolvedor em GS coleções

Infelizmente Java não é uma linguagem de programação funcional e não tem uma boa maneira de fazer o que quiser.

Eu acredito que o lib Apache Commons tem uma função chamada juntar que irá fazer o que quiser embora.

Ela terá de ser bom o suficiente para esconder o loop em um 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();
}

Eu suponho que você poderia fazê-lo 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);
}

Agora você pode usar String.join() com Java 8.

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

Com o apoio de lambdas que poderíamos fazer com o seguinte 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()));                  
    }

Abaixo está o código para dobrar a lista, mantendo volta as informações dos nós vamos para trás e dobrar à medida que avançamos.

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

Não existe tal função um, mas você poderia criar algo como o seguinte, e invocá-lo sempre que precisar.

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top