Java에서 목록 접기를 구현하는 방법
-
09-09-2019 - |
문제
목록이 있고 이를 단일 값으로 줄이고 싶습니다(함수 프로그래밍 용어 "fold", Ruby 용어 inject
), 좋다
Arrays.asList("a", "b", "c") ... fold ... "a,b,c"
나는 함수형 프로그래밍 아이디어(Scala)에 감염되었기 때문에 코드를 작성하는 것보다 더 쉽고 짧은 방법을 찾고 있습니다.
sb = new StringBuilder
for ... {
append ...
}
sb.toString
해결책
당신이 찾고있는 것은 문자열입니다 join()
Java 8.0부터 사용된 메서드입니다.아래 방법 중 하나를 시도해 보세요.
정적 방법
String#join(delimiter, elements)
:Collection<String> source = Arrays.asList("a", "b", "c"); String result = String.join(",", source);
개울 인터페이스는 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에 일부 기능적 측면을 적용하려는 경우 비록 당신은 할 수 있지만 람다J, 포크 조인(166y) 그리고 Google 컬렉션 해당 구문 설탕을 추가하는 데 도움이 되는 라이브러리입니다.
의 도움으로 Google 컬렉션 당신은 사용할 수 있습니다 조이너 수업:
Joiner.on(",").join("a", "b", "c")
Joiner.on(",")
불변 객체이므로 자유롭게 공유할 수 있습니다(예: 상수로).
다음과 같이 null 처리를 구성할 수도 있습니다. Joiner.on(", ").useForNull("nil");
또는 Joiner.on(", ").skipNulls()
.
큰 문자열을 생성하는 동안 큰 문자열을 할당하지 않으려면 이를 사용하여 기존 Streams, StringBuilders 등에 추가할 수 있습니다.통해 Appendable
인터페이스 또는 StringBuilder
수업:
Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");
맵을 작성할 때 항목과 키+값 사이의 구분을 위해 두 가지 서로 다른 구분 기호가 필요합니다.
Joiner.on(", ").withKeyValueSeparator(":")
.join(ImmutableMap.of(
"today", "monday"
, "tomorrow", "tuesday"))
당신이 찾고 있는 것은 불행히도 Java에는 없는 문자열 "join" 기능입니다.너무 어렵지 않아야 하는 자신만의 조인 기능을 굴려야 합니다.
편집하다: org.apache.commons.lang.StringUtils 유용한 문자열 함수(조인 포함)가 많이 있는 것 같습니다.
먼저 일반 펑터와 접기와 같은 기능적 프로젝션을 제공하는 Java용 기능 라이브러리가 필요합니다.나는 강력하면서도 단순한 라이브러리를 다음과 같이 설계하고 구현했습니다. 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.call에 대한 구현입니다(제 구현에서는 빈 문자열과 null인 경우 해당 대소문자를 제거하면 코드가 2줄로 줄어듭니다.
Seq.foldl의 실제 구현은 다음과 같습니다. Seq는 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 컬렉션 injectInto(Ruby와 같은), makeString 및 AppendString이 있습니다.다음은 귀하의 예에서 작동합니다.
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 컬렉션의 개발자입니다.
불행하게도 Java는 함수형 프로그래밍 언어가 아니며 원하는 작업을 수행할 수 있는 좋은 방법이 없습니다.
나는 Apache Commons lib에 다음이 있다고 생각합니다. Join이라는 함수 그래도 당신이 원하는 것을 할 것입니다.
메소드에서 루프를 숨길 만큼 충분해야 합니다.
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);
람다의 지원을 통해 다음 코드로 수행할 수 있습니다.
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 스타일(기능적):
// 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();
}
}