[TIL] 1월 18일

yeon·2021년 1월 18일
0

함수형 프로그래밍

아래 블로그 내용 정리

함수형 프로그래밍 기초 (1) 왜 함수형 프로그래밍인가

[JAVA 8] - 함수형 프로그래밍(Functional Programming)

  • 객체 지향 프로그래밍과 함수형 프로그래밍의 차이

    • 상태를 관리하는 점
    • 객체 지향은 객체 안에 상태를 저장해서 상태를 이용해서 기능을 구현한다.
    • 함수형 프로그래밍은 상태를 제어하기 보다는 상태를 저장하지 않고 없애는데 주력한다. 함수 자체를 입력값이 들어가면 특정한 출력값이 나오는것으로 상태를 저장하지 않는다.
  • 간결하게 코드를 작성할 수 있고, 객체 지향과 다른 방식으로 접근해야 한다.

  • 함수형프로그래밍은 몇몇 자료구조(list, set, map)을 이용해 최적화된 동작을 만든다

  • 위 블로그에서 예제 코드를 보았는데 이해가 안가서 스트림과 람다에 대해 학습해야할 것 같다.

  • 부수 효과를 없애고 순수 함수를 만들어 모듈화 수준을 높이는 프로그래밍 패러다임

    • 부수 효과란?
    • 주어진 값(매개변수) 이외의 외부 요소들이 프로그램 실행에 영향을 끼치지 않아야 함
    • 순수함수

1. 객체 지향은 명령형 프로그래밍이고, 함수형 프로그래밍은 선언형 프로그래밍이다.

  • 객체 지향 프로그래밍에서는 데이터를 어떻게 처리할지에 대해 명령을 통해 해결했다면, 함수형 프로그래밍은 선언적 함수를 통해 무엇을 풀어나가야할지 결정하는 것
List<String> myList = Arrays.asList("c1", "a2", "b3", "4", "5");

// 기존 방식
for(int i = 0; i < myList.size(); i++){
    String s = myList.get(i);
    if(s.startsWith("c")){
        System.out.println(s.toUpperCase());
    }
}
// stream API를 이용한 방식 
myList.stream()
        .filter(s -> s.startsWith("c"))
        .map(String::toUpperCase)
        .forEach(System.out::println);
  • stram API를 이용한 방식을 보면 단순하게 함수를 선언함으로써 데이터를 내가 원하는 뱡향으로 처리해 나아가고 있다.

2. 함수형 프로그래밍에서 함수는 1급 객체여야 한다.

  • 일급 객체 (first-class citizens, 일급 시민)
    • 컴퓨터 프로그래밍 언어 디자인에서 일반적으로 다른 객체들에 적용 가능한 연산을 모두 지원하는 객체를 가르킨다. (무슨말인지 모르겠다...)
    • 사용할 때 다른 요소들과 아무런 차별이 없다는 것을 뜻함
  • 1급 객체란 다음 조건을 만족하는 객체이다.

    • 변수나 데이터 구조안에 담을 수 있다.
    • 파라미터로 전달할 수 있다.
    • 반환값(return value)로 사용할 수 있다.
    • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
    • 동적으로 프로퍼티 할당이 가능하다. (프로퍼티 : 속성)
    • 기존 데이터의 불변성(Immutable)
  • 함수형 프로그래밍은 코드의 간결성이 증가 되었다.

블로그에 있는 예제코드들을 이해하기 위해서 람다식과 스트림에 대한 이해가 필요하다고 생각해서 자바의 정석 ch14 람다와 스트림 파트를 학습하였다.

함수형 인터페이스 (797p)

  • 함수형 인터페이스는 단 하나의 추상 메서드만 가질 수 있다.

  • 람다식은 익명객체이기 때문에 람다식을 다루기 위해서는 참조 변수가 필요하다.

    • 참조 변수의 타입은 함수형 인터페이스로 한다.

    • 단, 함수형 인터페이스의 메서드와 람다식의 매개변수 개수와 반환타입이 일치해야한다.

    • 아래의 두 코드를 잘 비교해보자.

      interface MyFunction {
      		int max (int a, int b);
      }
      
      public class Exam {
      		// MyFunction 인터페이스를 구현한 익명 객체 생성 
      		MyFunction f = new MyFunction() {
      						public int max(int a, int b) {
      								return a > b ? a : b;
      						}
      		};
      		int value = f.max(5, 3);   // 익명 객체의 메서드를 호출
      }
    • 위의 코드는 MyFunction 인터페이스를 정의하고, 이 인터페이스를 구현한 익명 클래스의 객체를 생성한 것이다.

      • 익명 클래스 : 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스 (일회용)
    • 위의 코드를 람다식을 이용하여 아래와 같이 바꿀 수 있다.

      @FunctionalInterface 
      interface MyFunction {
      		int max (int a, int b);
      }
      
      public class Exam {
      		MyFunction f = (a, b) -> a > b ? a : b;
      		int value = f.max(3, 5);
      }
    • 코드가 훨씬 간결해졌다.

    • 람다식은 익명 객체이다.

    • @FuncionalInterface 어노테이션을 추가해주면 알아서 컴파일러가 함수형 인터페이스 조건에 맞는지 검사해준다.

    • MyFunction 타입의 참조변수 f는 람다식을 다루기 위한 참조변수 이다.

함수형 인터페이스 타입의 매개변수와 반환타입(798p)

  • 매개변수가 함수형 인터페이스인 경우
    • 메서드의 매개변수로 람다식을 받겠다는 뜻
  • 반환타입이 함수형 인터페이스인 경우
    • 이 메서드는 람다식을 반환한다는 뜻
@FunctionalInterface
interface MyFunction {
    void run();
}

class LamdaEx1 {
    // 매개변수 타입이 MyFunction(함수형 인터페이스)인 메서드
    static void execute(MyFunction f) {
        f.run();
    }
    // 반환타입이 MyFunction(함수형 인터페이스)인 메서드
    static MyFunction getMyFunction() {
        return () -> System.out.println("f2.run()");
    }

    public static void main(String[] args) {
        // 람다식으로 MyFunction의 run() 구현
        MyFunction f1 = () -> System.out.println("f1.run()");

        MyFunction f2 = getMyFunction();

        f1.run();
        f2.run();
        System.out.println();
        execute(f1);
        execute(() -> System.out.println("run"));
    }
}

</> 실행 결과
f1.run()
f2.run()

f1.run()
run
  • static void execute(MyFunction f) {
    f.run();
    }

    • 매개변수의 타입이 MyFunction 즉, 함수형 인터페이스 이다.
    • 매개변수로 람다식을 받겠다는 뜻
    • f.run();
      • 람다식을 호출하는 것
    • 람다식에 execute 라는 이름을 붙여주고 이를 호출하는 메서드이다.
  • static MyFunction getMyFunction() {
    return () -> System.out.println("f2.run()");
    }

    • 반환타입이 MyFunction, 함수형 인터페이스
    • 이 메서드는 람다식을 반환한다는 뜻
  • MyFunction f1 = () -> System.out.println("f1.run()");

    • 람다식으로 MyFunction 인터페이스의 run()을 구현한 것
  • execute(() -> System.out.println("run"));

    • 이 부분을 보면 람다식을 매개변수로 받는 execute() 메서드의 인자를 람다식으로 넣은 것을 볼 수 있다.
    • 이렇게 쓰는군

java.util.function 패키지(802p)

java.util.function 패키지에 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 정의해놓음

조건식에 표현에 사용되는 Predicate

  • 반환타입이 boolean
  • 조건식을 람다식으로 표현하는데에 사용
Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";

if(isEmptyStr.test(s)){
    System.out.println("This is an empty String.");
 }

매개변수가 두개인 함수형 인터페이스

  • 인터페이스 이름 앞에 'Bi'
  • Supplier는 매개변수는 없고 반환값만 존재하는데, 메서드는 반환값 두개가 될 수 없으므로 BiSupplier 는 존재하지 않음
  • 두개를 초과하는 매개변수를 갖는 함수형 인터페이스가 필요하면 직접 만들어서 써야함

UnaryOperator 와 BinaryOperator

  • 매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스
  • 이들의 조상은 각각 Function 과 BiFunction 임

스트림 API

아래 블로그 보고 학습 정리

정리가 굉장히 잘되어 있는 것 같다.

자바

  1. 자바 스트림(Stream) 개요

1) 스트림이란?

  • 데이터의 흐름으로 배열 또는 컬렉션 인스턴스에 함수를 조합하여 원하는 결과를 필터링하고 가공된 결과를 손쉽게 처리 가능

  • 기존 방식을 이용한 출력

    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    public class StreamExam {
        public static void main(String[] args) {
            String[] strArr = {"data1", "data2", "data3"};
            List<String> strList = Arrays.asList(strArr);
    
            // 기존 방식, for문을 이용해 결과 출력
            Arrays.sort(strArr);
            Collections.sort(strList);
    
            for(String str : strArr){
                System.out.println(str);
            }
    
            for(String str : strList) {
                System.out.println(str);
            }
        }
    }
    • Stream API 사용

      // Stream API 사용
      strList.stream().sorted().forEach(System.out::println);
      Arrays.stream(strArr).sorted().forEach(System.out::println);
              
      // 람다식으로 표현
      strList.stream().sorted().forEach(x -> System.out.println(x));
      Arrays.stream(strArr).sorted().forEach(x -> System.out.println(x));
    • 데이터 소스(배열 혹은 리스트)로 부터 스트림을 생성하고,

      정렬을 위해 sorted()메서드를 호출하고,

      출력을 위해 forEach()메서드 호출


오늘 한 일

  • 객체지향 프로그래밍과 함수형 프로그래밍의 차이에 대해 학습 (객체 지향 프로그래밍은 명령에 의해 동작, 함수형 프로그래밍은 함수를 선언함으로서 무엇을 풀어나갈지 결정하는 것)
  • 람다식 작성 방법, 함수형 인터페이스란?, 람다식의 참조변수 타입은 무엇으로?, 매개변수와 반환타입이 함수형 인터페이스인 경우?

6개의 댓글

comment-user-thumbnail
2021년 1월 18일

수고하셨습니다. 공부한 양이 엄청 많네요!

1개의 답글
comment-user-thumbnail
2021년 1월 18일

stream API 여기 그림도 이해하기 괜찮은 거 같아요!
https://velog.io/@kskim/Java-Stream-API

1개의 답글
comment-user-thumbnail
2021년 1월 20일

정리를 되게 깔끔하게 잘하셨네요!~ 어노테이션으로 함수형 인터페이스 인지 확인할 수 있는건 처음알았네요 :)

1개의 답글