سؤال

لدي قائمة وتريد للحد منه إلى قيمة واحدة (البرمجة الوظيفية مصطلح "أضعاف" ، روبي الأجل inject) مثل

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

كما أنا مصابة البرمجة الوظيفية الأفكار (سكالا) ، أنا أبحث عن أسهل/أقصر طريق إلى رمز من

sb = new StringBuilder
for ... {
  append ...
}
sb.toString
هل كانت مفيدة؟

المحلول

ما تبحث عنه هو سلسلة join() الطريقة التي جافا لها منذ 8.0. جرب إحدى الطرق أدناه.

  1. طريقة ثابتة String#join(delimiter, elements):

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
  2. مجرى واجهة تدعم عملية أضعاف مشابهة جدا ل Scala foldLeft وظيفة. ألق نظرة على الاستسلام التالي جامع:

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

    قد ترغب في استيراد ثابت Collectors.joining لجعل الكود الخاص بك أوضح.

    بالمناسبة يمكن تطبيق هذا الجامع على مجموعات من أي كائنات معينة:

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

نصائح أخرى

للإجابة على سؤالك الأصلي:

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

حيث تبدو F مثل هذا:

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

كما اقترح DFA، جاوة الوظيفية وقد هذا ينفذ، وأكثر من ذلك.

مثال 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(",")));

مثال 2:

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

مثال 3:

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

منح

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

ثم استخدام يبدو فقط

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

إذا كنت ترغب في تطبيق بعض الجوانب الوظيفية ل Java القديم العادي، دون تبديل اللغة على الرغم من أنك استطعت لامدراج, شوكة الانضمام (166Y) و مجموعات جوجل هي المكتبات التي تساعدك على إضافة هذا السكر النحوي.

بمساعدة مجموعات جوجل يمكنك استعمال ال نجار فئة:

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

Joiner.on(",") هو كائن ثابت حتى تقوم بمشاركته بحرية (على سبيل المثال كمستمر ثابت).

يمكنك أيضا تكوين معالجة NULL مثل Joiner.on(", ").useForNull("nil"); أو Joiner.on(", ").skipNulls().

لتجنب تخصيص سلاسل كبيرة أثناء إنشاء سلسلة كبيرة، يمكنك استخدامها لإلحاق الجداول الموجودة، و Stringbuilders، إلخ. من خلال Appendable واجهة أو StringBuilder صف دراسي:

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

عند كتابة الخرائط، تحتاج إلى فصلين مختلفين للإدخالات والفصل بين قيمة المفتاح +:

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

ما تبحث عنه هو وظيفة "انضمام" سلسلة، لسوء الحظ، جافا لا تملك. سيكون عليك لفة وظيفة الانضمام الخاصة بك والتي لا ينبغي أن تكون صعبة للغاية.

يحرر: org.apache.commons.lang.stringutils. يبدو أن لديها العديد من وظائف السلسلة المفيدة (بما في ذلك الانضمام).

لسوء الحظ في جافا، لا يمكنك الهروب من هذه الحلقة، وهناك العديد من المكتبات. على سبيل المثال، يمكنك تجربة العديد من المكتبات:

أولا سوف تحتاج وظيفية مكتبة جافا التي اللوازم العامة functors وظيفية التوقعات مثل أضعاف.لقد صممت ونفذت قوية (بحكم) بعد بسيطة مثل هذه المكتبة هنا: http://www.codeproject.com/KB/java/FunctionalJava.aspx (لقد وجدت المكتبات الأخرى المذكورة معقدة أكثر من اللازم).

ثم الحل الخاص بك سوف تبدو مثل:

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"

ملاحظة هذا من خلال تطبيق أضعاف ، الجزء الوحيد الذي يحتاج حقا إلى أن تكون مدروسة على تنفيذ Func2.المكالمة 3 خطوط من التعليمات البرمجية التي تحدد المشغل قبول تراكم عنصر والعودة المجمع (بلدي تنفيذ حسابات فارغة و بالقيم الخالية ، إذا قمت بإزالة هذه الحالة إلى 2 الأسطر من التعليمات البرمجية).

وهنا التنفيذ الفعلي وما يليها.foldl, بعدها تنفذ 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;
}

مجموعات GS. وقد بحقنها (مثل روبي) ومكترين وملهبات. ما يلي سيعمل مع مثالك:

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

ملاحظة: أنا مطور على مجموعات GS.

لسوء الحظ، ليست جافا لغة برمجة وظيفية ولن يكون لها طريقة جيدة للقيام بما تريد.

وأعتقد أن Apache Commons Lib لديه وظيفة تسمى الانضمام هذا سوف تفعل ما تريد رغم ذلك.

يجب أن تكون جيدة بما يكفي لإخفاء الحلقة في طريقة.

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

أفترض أنك تستطيع أن تفعل ذلك بشكل متكرر:

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

الآن يمكنك استخدام String.join() مع جافا 8.

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

بدعم من Lambdas يمكننا أن نفعل مع الكود التالي:

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

فيما يلي رمز طي القائمة، عن طريق الاستراد إلى معلومات العقد، السماح للقلق والمضي قدما.

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 (وظيفية):

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

لا توجد وظيفة مثل هذه الوظيفة، ولكن يمكنك إنشاء شيء مثل ما يلي، واستدعائه كلما احتجت إليه.

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

    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top