검색결과 리스트
분류 전체보기 에 해당되는 글 580건
- 2020.11.10 오브젝트 핸들(Handle)을 이용한 커널 오브젝트의 조작
- 2020.11.03 의미있게 구분하라
- 2020.11.03 그릇된 정보를 피하라
- 2020.10.29 IntelliJ 에서 배경색 변경하기
- 2020.10.29 람다식의 매개변수
- 2020.10.16 람다식 호출
- 2020.10.16 함수의 호출 방법
- 2020.10.13 매개변수에 람다식 함수를 이용한 고차 함수
- 2020.10.13 람다식을 인자나 반환값으로 사용하는 고차 함수
- 2020.10.13 일반 함수를 인자나 반환값으로 사용하는 고차 함수
- 2020.10.07 고차 함수
- 2020.10.07 일급 객체
- 2020.10.07 람다식
- 2020.10.06 함수형 프로그래밍
- 2020.10.06 부스트 코스 사이트
우리가 알고 있는 리소스는 프로세스 밖에 없기 때문에 프로세스에 관련된 커널 오브젝트 조작 함수들을 몇 가지 소개한다
* 프로세스의 우선순위(Priority) 변경
프로세스 우선순위를 높여보도록 하겠다
커널 오브젝트에 저장된 우선순위 정보를 변경시키는 것이다
BOOL SetPriorityClass(
HANDLE hProcess,
DWORD dwPriorityClass);
hProcess
컴파일러나 인터프리터만 통과하려는 생각으로 코드를 구현하는 프로그래머는 스스로 문제를 일으킨다
예를 들어, 동일한 범위 안에서는 다른 두 개념에 같은 이름을 사용하지 못한다
그래서 프로그래머가 한쪽 이름을 마음대로 바꾸고픈 유혹에 빠진다
어떤 프로그래머는 철자를 살짝 바꿨다가 나중에 철자 오류를 고치는 순간 컴파일이 불가능한 상황에 빠진다
컴파일러를 통과할지라도 연속된 숫자를 덧붙이거나 불용어를 추가하는 방식은 적절하지 못하다
이름이 달라져야 한다면 의미도 달라져야 한다
연속적인 숫자를 덧붙인 이름(a1, a2, ..., aN)은 의도적인 이름과 정반대다
이런 이름은 그릇된 정보를 제공하는 이름도 아니며, 아무런 정보를 제공하지 못하는 이름일 뿐이다
public static void copyChars(char a1[], char a2[]) {
for(int i = 0; i < a1.length; ++i)
a1[i] = a2[i];
}
}
함수 인수 이름으로 source와 destination을 사용한다면 코드 읽기가 훨씬 더 쉬워진다
불용어를 추가한 이름 역시 아무런 정보도 제공하지 못한다
Product라는 클래스가 있다고 가정하자
다른 클래스를 ProductInfo 혹은 ProductData라 부른다면 개념을 구분하지 않은 채 이름만 달리한 경우다
Info나 Data는 a, an, the와 마찬가지로 의미가 불분명한 불용어다
요지는 zork라는 변수가 있다는 이유만으로 theZork라 이름지어서는 안된다는 말이다
불용어는 중복이다
변수 이름에 variable이라는 단어는 단연코 금물이다
표 이름에 table이라는 단어도 마찬가지다
NameString이 Name보다 뭐가 나은가?
Name이 부동 소수가 될 가능성이 있던가?
그렇다면 앞서 언급한 그릇된 정보를 피하라 규칙을 위반한다
Customer라는 클래스와 Customer Object라는 클래스를 발견했다면 차이를 알겠는가?
고객 급여 이력을 찾으려면 어느 클래스를 뒤져야 빠를까?
이와 같은 오류를 저지르는 애플리케이션이 있다
개발자를 보호하고자 이름을 바꿨으나 오류 형태는 정확히 다음과 같다
getActiveAccount( );
getActiveAccounts( );
getActiveAccountInfo( );
명확한 관례가 없다면 변수 moneyAmount는 money와 구분이 안 된다
custormerInfo는 custorer와, accountData는
프로그래머는 코드에 그릇된 단서를 남겨서는 안 된다
그릇된 단서는 코드 의미를 흐린다
나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해도 안 된다
여러 계정을 그룹으로 묶을 때, 실제 List가 아니라면 accountLIst라 명명하지 않는다
프로그래머에게 List는 특수한 의미다
계정을 담는 컨테이너가 실제 List가 아니라면 프로그래머에게 그릇된 정보를 제공하는 셈이다
그러므로 accountGroup, bunchOfAccounts, 아니면 단순히 Accounts라 명명한다
서로 흡사한 이름을 사용하지 않도록 주의한다
한 모듈에서 XYZControllerForEfficientHandlingOfStrings라는 이름을 사용하고,
조금 떨어진 모듈에서 XYZControllerForEfficientStorageOfStrings 라는 이름을 사용한다면?
차이를 알아챘는가?
두 단어는 겁나게 비슷하다
유사한 개념은 유사한 표기법을 사용한다
이것도 정보다
일관성이 떨어지는 표기법은 그릇된 정보다
십중팔구 개발자는 이름만 보고 객체를 선택한다
화면에서 프로젝트를 닫고 File -> Close Project
다음 화면에서 Configure -> Settings 를 가서 다음을 변경한다
Appearance -> Theme 에서 색상을 고르고 Apply 버튼을 누르면 된다
매개변수 개수에 따라 람다식을 구성하는 방법
매개 변수와 인자 개수에 따라 람다식의 생략된 표현이 가능
* 람다식에 매개변수가 없는 경우
noParam({"Hello World!"})
fun noParam(out: () -> String) = println(out())
noParam( ) 함수의 매개 변수는 람다식 1개를 가지도 있는데 이때는 함수 사용시 소괄호 생략 가능
이 람다식에는 매개변수가 없으므로 화살표(->) 기호가 사용되지 않음
그리고 여기서 소괄호는 생략 가능
매개변수는 없지만 반환 자료형은 문자열을 반환
따라서 println( )에 의 해 "Hello World" 가 출력
* 람다식의 매개변수가 1개인 경우
람다식에 매개변수가 1개 있을 경우 람다식에 화살표(->) 기호 왼쪽에 필요한 변수를 써줘야 함
fun main( ) {
oneParam({ a -> "Hello World! $a" })
oneParam { a -> "Hello World! $a" } // 위와 동일한 결과, 소괄호 생략 가능
oneParam { "Hello World! $it" } // 위와 동일한 결과, it으로 대체 가능
}
// 매개 변수가 1개 있는 람다식이 oneParam( ) 함수의 매개변수 out으로 지정됨
fun oneParam(out: (String) -> String) {
println(out("OneParam"))
}
* 람다식의 매개 변수가 2개 이상인 경우
fun main( ) {
// 매개변수가 2개 있는 람다식
moreParam { a, b -> "Hello World! $a $b } // 매개 변수 이름 생략 불가
}
// 매개변수가 2개 있는 람다식의 moreParam 함수의 매개변수로 지정됨
fun moreParam(out: (String, String) -> String) {
println(out("OneParam", "TwoParam"))
}
만일 특정 람다식의 매개변수를 사용하고 싶지 않을 때는 이름 대신에 언더스코어(_)로 대체
moreParam { _, b -> "Hello World! $b" } // 첫 번째 문자열은 사용하지 않고 생략
* 일반 매개 변수와 람다식 매개 변수 같이 사용하기
fun main( ) {
// 1. 인자와 함께 람다식을 사용하는 경우
withArgs("Arg1", "Arg2", { a, b -> "Hello World! $a, $b })
// 2. withArgs( ) 함수의 마지막 인자가 람다식인 경우 소괄호 바깥으로 분리 가능
withArgs("Arg1", "Arg2") { a, b -> "Hello World $a, $b }
}
// withArgs( ) 함수는 일반 매개 변수 2개를 포함, 람다식을 마지막 매개변수로 가짐
fun withArgs(a: String, b: String, out: (String, String) -> String) {
println(out(a, b))
}
* 일반 함수에 람다식 매개변수를 2개 이상 사용하기
마지막 인자는 소괄호 밖에 둘 수 있음
({첫 번째}, {두 번째})
({첫 번째}) {두 번째}
람다식 함수가 3개가 되었을 때도 마지막 람다식만 빼내어 다음과 같이 구성
({첫 번째}, {두 번째}) {세 번째}
* 값에 의한 호출
코틀린에서 값에 의한 호출은 함수가 또 다른 함수의 인자로 전달될 경우
람다식 함수는 값으로 처리되어 그 즉시 함수가 수행된 후 값을 전달
이렇게 함수나 람다식의 함수 형태를 인자로 사용하면
즉시 호출되어 실행된 후 그 값이 사용됨
val result = callByValue(lamda()) // 람다식 함수를 호출
fun callByValue(b: Boolean): Boolean { } // 일반 변수 자료형으로 선언된 매개변수
val lamda: ( ) -> Boolean = { }
* 이름에 의한 람다식 호출
람다식의 이름이 인자로 전달될 때 실행되지 않고 실제로 호출할 때 실행되도록 하면 어떨까?
val result = callByName(otherLamda) // 람다식 이름으로 호출
fun callByName(b: ( ) -> Boolean): Boolean { } // 람다식 자료형으로 선언된 매개변수
otherLamda: ( ) -> Boolean = { }
위의 코드와 거의 동일해 보이지만 람다식의 이름을 callByName( ) 함수에서 호출하는 점이 다름
이것을 잘 활용하면 상황에 맞춰 즉시 실행할 필요가 없는 코드를 작성하는 경우
이름에 의한 호출 방법을 통해 필요할 때만 람다식이 작동하도록 만들 수 있음
* 다른 함수의 참조에 의한 일반 함수 호출
람다식이 아닌 일반 함수를 또 다른 함수의 인자에서 호출하는 고차 함수의 경우를 생각
fun sum(x: Int, y: Int) = x + y
덧셈을 하는 평범한 함수
이것을 고차함수인 funcParam( ) 에서 호출
funcParam(3, 2, sum) // 오류! sum은 람다식이 아님
fun funcParam(a: Int, b: Int, c: (Int, Int) -> Int): Int {
return c(a, b)
}
sum 함수는 람다식이 아니므로 위와 같이 이름으로 호출할 수 없음
하지만 sum( )과 funcParam( )의 매개변수 c의 선언부 구조를 보면 인자 수와 자료형의 개수가 동일
이 때는 다음과 같이 콜론 기호를 함수 앞에 사용해 소괄호와 인자를 생략하고 사용
funcParam(3, 2, ::sum)
기본형 변수로 할당된 값은 스택에 있음
다른 함수에 인자로 전달하는 경우에는 해당 값이 복사되어 전달
참조형 변수로 할당된 객체는 참조 주소가 스택에 있고 객체는 힙에 있음
참조형 객체는 함수에 전달할 때는 참조된 주소가 복사되어 전달됨
JVM에서 실행되는 자바나 코틀린은 함수를 호출할 때
인자의 값만 복사하는 '값에 의한 호출'(Call by Value)이 일반적
C++에서 사용하는 포인터 주소 연산이 없기 때문에 주소 자체를 사용해 호출하는
'참조에 의한 호출(Call by Reference)'은 자바나 코틀린에서 사용되지 않음
자바는 객체가 전달될 때 주소 자체를 전달하는 것이 아닌 값을 복사하는데 이것은 참조에 의한 호출처럼 보이지만 그 값이 주소일 뿐
코틀린은 람다식을 사용하면서 몇 가지 확장된 호출 방법을 사용할 수 있음
var result: Int
result = highOrder({x, y -> x + y}, 10, 20) // 람다식을 매개변수와 인자로 사용
fun highOrder(sum: (Int, Int) -> Int, a: Int, b: Int): Int {
return sum(a, b)
}
* 변수에 할당하는 람다식 작성
val multi = {x: Int, y: Int -> x * y} // 일반 변수에 람다식 할당
var result = multi(10, 20) // 람다식이 할당된 변수는 함수처럼 사용 가능
변수 multi는 x와 y를 인자로 받아 곱하여 변환하는 람다식이 할당
multi 변수는 자료형이 생략되어 있지만 (Int, Int) -> Int에 의해 Int 형으로 자료 추론
람다식이 변수에 할당되어 변수 이름이 multi( )와 같이 함수 형태로 사용
multi(10, 20)에 전달된 10과 20은 람다식 x, y에 전달
그런 다음 화살표( -> ) 기호 오른쪽의 연산 x * y에 의해 두 값을 곱하여 반환
람다식의 자료형 선언 람다식의 매개 변수 람다식의 처리 내용
val multi: (Int, Int) -> Int = {x: Int, y: Int -> x * y}
변수를 함수처럼 람다식의 선언 자료형은 선언 자료형이 함수의 내용과 결과
사용 가능 매개변수에 자료형이 명시되어 있으면 반환, 표현식이 여러
명시된 경우 생략 가능 생략 가능 줄인 경우 마지막
표현식이 반환
화살표( -> )를 기준으로 왼쪽에는 람다식의 매개변수 x: Int, y: Int를 작성
오른쪽에는 함수의 내용과 반환 값 x * y를 적음
함수 내용에 표현식이 2줄 이상이라면 마지막 표현식이 반환
val multi2: (Int, Int) -> Int = {x: Int, y: Int ->
println("x * y")
x * y // 마지막 표현식이 반환
}
람다식의 매개변수에 자료형이 지정되어 있다면 변수의 자료형은 생략
val multi: (Int, Int) -> Int = {x: Int, y: Int -> x * y} // 생략되지 않은 전체 표현
val multi = {x: Int, y: Int -> x * y} // 선언 자료형 생략
val multi = (Int, Int) -> Int = {x, y -> x * y} // 람다식 매개변수 자료형 생략
* 반환 자료형이 아예 없거나 매개변수가 하나만 있을때 표현 방법
val greet: ( ) -> Unit = { println("Hello World!") }
val square: (Int) -> Int = { x -> x * x }
첫 번째 람다식에는 매개 변수와 반환값이 없음
그래서 greet의 자료형은 ( ) -> Unit
람다식 본문을 보면 매개변수를 표현할 필요가 없으므로
화살표 앞쪽과 화살표 자체가 생략
변수 square는 (Int) -> Int로 람다식의 자료형을 지정
람다식을 보고 매개변수와 반환값을 추론할 수 있다면 람다식의 매개변수 자료형은 생략
* 람다식 안의 람다식
val nestedLamda: ( ) -> ( ) -> Unit = {{println("nested")}}
아무것도 없는 람다식 { }에 람다식 {println("nested")}를 넣음
// 람다식의 자료형 생략
val greet = {println("Hello World!")} // 추론 가능
val square = {x: Int -> x * x} // square의 자료형을 생략하려면 x의 자료형을 명시
val nestedLamda = {{println("nested")}}
* 인자에 일반 함수 사용하기
val res = mul(sum(3, 3), 3) // 인자에 함수 사용
fun sum(a: Int, b: Int) = a + b
fun mul(a : Int, b: Int) = a * b
mul( ) 함수의 첫 번째 인자에 sum( ) 함수를 사용
* 반환 값에 일반 함수 사용하기
fun sum(a : Int, b: Int) = a + b
fun funcFunc( ): Int { // 함수의 반환값으로 함수 사용
return sum(2, 2)
}
funcFunc( ) 함수의 반환 값으로 sum( ) 함수를 사용
* 고차 함수 (High-order Function)
- 다른 함수를 인자로 사용하거나 함수를 결과값으로 반환하는 함수
- 일급 객체 또는 일급 함수를 서로 주고 받을 수 있는 함수
예)
fun main( ) {
println(highFunc({x, y -> x + y }, 10, 20)) // 람다식 함수를 인자로 넘김
}
// sum 매개변수는 함수
fun highFunc(sum: (Int, Int) -> Int, a: Int, b: Int): Int = sum(a, b)
highFunc( ) 함수는 sum이라는 매개 변수가 있음
하지만 이 sum은 람다식 함수 형식으로 선언
즉 highFunc( ) 함수는 sum을 통해서 람다식 함수를 인자로 받아들이는 고차함수
* 일급 객체 (First Class Citizen)
- 일급 객체는 함수의 인자로 전달할 수 있음
- 일급 객체는 함수의 반환값에 사용할 수 있음
- 일급 객체는 변수에 담을 수 있음
만약 함수가 일급 객체이면 일급 함수라고 부름
일급 함수에 이름이 없는 경우 '람다식 함수' 또는 '람다식'이라고 부름
* 람다식
람다식은 람다 대수(Lamda Calculus)에서 유래한 것
{ x, y -> x + y } // 람다식의 예(이름이 없는 함수 형태)
위의 식을 보면 함수의 이름이 없고 화살표 (->)가 사용
수학에서 말하는 람다 대수는 이름이 없는 함수
2개 이상의 입력을 1개의 출력으로 단순화한다는 개념
함수형 프로그래밍의 람다식
- 다른 함수의 인자로 넘기는 함수
- 함수의 결과값을 반환하는 함수
- 변수에 저장하는 함수
* 람다 대수
변수의 네임 바인딩과 대입의 방법을 이용하여 함수 정의, 함수 적용, 귀납적 함수 추상화를 수행하고 수학 연산을 표현하는 형식
* 함수형 프로그래밍
- 순수 함수를 작성하여 프로그램의 부작용을 줄이는 프로그래밍 기법
- 함수형 프로그래밍에서는 람다식과 고차 함수를 사용
- 순수 함수를 사용
- 람다식, 고차 함수를 사용 가능
* 순수 함수
- 부작용이 없는 함수 : 함수가 같은 인자에 대하여 항상 같은 결과를 반환
- 순수 함수 : 부작용이 없는 함수가 함수 외부의 어떤 상태도 바꾸지 않음
예)
fun sum(a: Int, b: Int): Int {
return a + b // 동일한 인자인 a, b를 입력받아 항상 a + b를 출력 (부작용이 없음)
}
함수 안에서 함수 외부의 어떤 변수 상태도 바꾸지 않음
* 순수 함수의 조건
- 같은 인자에 대하여 항상 같은 값을 반환
- 함수 외부의 어떤 상태도 바꾸지 않음
사실 프로그램이 커지다 보면 부작용을 완벽하게 통제할 수 있는
순수 함수를 만들기 어려움
평소에 가능한 한 순수 함수에 가깝게 안전한 함수를 구현하려고 노력
예) 순수 함수가 아닌 함수의 예
fun check( ) {
val test = User.grade() // check( ) 함수에 없는 외부의 User 객체를 사용
// 변수 test는 User.grade( )의 실행 결과에 따라 달라짐
if (test != null) process(test)
}
check( ) 함수는 함수 안에서 함수 외부에 있는 User 객체의 함수인 grade( ) 실행
또 grade( )의 함수의 결과값을 test에 저장하여 조건문에 사용
심지어 process( ) 함수는 조건을 만족하지 못하면 실행되지 않음
쉽게 말해 check( ) 함수의 실행 결과를 예측하기 어려움
이런 함수가 순수 함수의 조건을 만족하지 못함