[TIL] 1월 18일
함수형 프로그래밍
아래 블로그 내용 정리
함수형 프로그래밍 기초 (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
아래 블로그 보고 학습 정리
정리가 굉장히 잘되어 있는 것 같다.
- 자바 스트림(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()메서드 호출
-
오늘 한 일
- 객체지향 프로그래밍과 함수형 프로그래밍의 차이에 대해 학습 (객체 지향 프로그래밍은 명령에 의해 동작, 함수형 프로그래밍은 함수를 선언함으로서 무엇을 풀어나갈지 결정하는 것)
- 람다식 작성 방법, 함수형 인터페이스란?, 람다식의 참조변수 타입은 무엇으로?, 매개변수와 반환타입이 함수형 인터페이스인 경우?
Author And Source
이 문제에 관하여([TIL] 1월 18일), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yeon/TIL-1월-18일저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)