트랙
/
Julia
Julia
/
연습 문제
/
고등학교 연인
고등학교 연인

고등학교 연인

학습 연습 문제

소개

Julia는 가능한 한 많은 코드를 JIT 컴파일할 수 있는 함수 안에 넣도록 권장해요. 그리고 작은 함수를 많이 만드는 것이 설계상 성능이 좋아요.

그러면 작고 단순한 함수가 많이 남게 되고, 이런 함수들을 조합해야 복잡한 작업을 처리할 수 있어요.

한 가지 확실한 방법은 함수 호출을 중첩하는 거예요. 다음 예제는 억지로 만든 감이 있지만, 요점을 잘 보여줘요.

julia> first.(titlecase.(reverse.(["my", "test", "strings"])))
3-element Vector{Char}:
 'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
 'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)

이 방법의 단점은 중첩이 깊어질수록 가독성이 빠르게 떨어진다는 거예요.

더 단순하고 유연한 방법이 필요해요.

합성

수학자들이 아주 좋아하는 기법이고, Julia는 수학의 문법을 그대로 가져왔어요.

원하는 만큼의 함수를 ∘ 연산자로 composed할 수 있어요 (\circ를 입력한 뒤 탭을 눌러요). 그 결과는 하나의 함수처럼 사용할 수 있어요.

julia> compfunc = first ∘ titlecase ∘ reverse
first ∘ titlecase ∘ reverse
julia> compfunc.(["my", "test", "strings"])
3-element Vector{Char}:
 'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
 'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)
# alternative syntax, giving the same result
julia> (first ∘ titlecase ∘ reverse).(["my", "test", "strings"])

몇 가지 짚어 볼 점이 있어요:

  • 처음에 나열한 함수들은 중첩할 때와 같은 순서로 나타나고, 오른쪽에서 왼쪽 순서로 실행돼요.
  • 브로드캐스팅은 합성할 때는 사용하기 간단하지 않지만, 합성된 함수를 호출할 때 적용할 수 있어요.

파이프라이닝

또 다른 방법은 _수학자_보다는 _프로그래머_의 접근 방식이라고 할 수 있어요.

Pipelines는 오래전부터 Unix 셸 스크립트에서 사용되어 왔고, 최근에는 주류 프로그래밍 언어에서도 인기를 얻었어요 (F#이 그 도입을 개척했다는 평가를 받기도 해요).

기본 개념은 어떤 데이터에서 시작해서 일련의 함수들을 거쳐 결과를 얻는 거예요.

파이프 연산자는 |>예요 (F#과 최근 버전의 R에서처럼요). Julia에는 브로드캐스트 버전인 .|>도 있어요.

julia> ["my", "test", "strings"] .|> reverse .|> titlecase .|> first
3-element Vector{Char}:
 'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
 'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)

이제 실행은 엄격하게 왼쪽에서 오른쪽으로 진행되고, 각 함수의 출력이 화살표 방향으로 흘러 다음 함수의 입력이 돼요.

한계, 우회 방법, 그리고 다른 선택지

합성과 파이프라이닝을 설명할 때 사용한 함수들이 모두 단일 인자를 받는다는 건 우연이 아니에요.

몇몇 순수 함수형 언어는 첫 번째 인자를 함수로 파이프하되, 다른 인자들도 포함할 수 있게 해요.

반대로 Julia는 파이프라인에서 함수 이름(또는 그에 상응하는 것)만 기대하고, 추가 인자는 받지 않아요.

여기에는 중요한 기술적 이유가 있어요 (currying이 언어 설계의 표준적인 부분이 아니라는 점과 관련이 있어요). 커링을 전혀 이해하지 못하는 많은 사람은 이 한계가 부주의한 실수가 아니며, 향후 Julia 버전에서 바뀔 가능성도 낮다는 점을 그냥 받아들이면 돼요.

우회 방법

필요한 일을 해 주는 단일 인자 함수가 필요해요. 다행히 Julia에서는 새 함수를 정의하기가 쉬워요.

가장 간단하게는 anonymous function을 사용할 수 있어요. 예를 들어, 입력 문자열 하나가 있고 밑줄을 기준으로 나누고 싶다면:

julia> "my_test_strings" |> (s -> split(s, '_'))
3-element Vector{SubString{String}}:
 "my"
 "test"
 "strings"

그 배열은 이전처럼 다른 함수로 파이프할 수 있어요.

이 경우 익명 함수를 괄호로 감싸는 건 선택 사항이지만, 일반적으로 모호함을 줄이는 데 유용한 방법이에요.

마찬가지로, 프로그램 앞부분에서 이름 있는 함수를 만들어 필요할 때마다 재사용할 수도 있어요.

지침

이 연습 문제에서는 고등학교 커플이 소셜 미디어에서 사랑을 고백할 수 있도록, 두 사람의 이니셜이 들어간 유니코드 하트를 만들어 줄 거예요:

❤ J.  +  M. ❤

1. 이름 정리하기

cleanupname 메서드를 구현해요. 이름을 받아서 - 문자를 모두 없애고 공백으로 바꿔요. 이름의 앞뒤에 있는 공백도 제거해요.

julia> cleanupname("Jane-Ann")
"Jane Ann"

2. 이름의 첫 글자 가져오기

firstletter 메서드를 구현해요. 이름을 받아서 첫 글자를 문자열로 반환해요. 이전 단계에서 정의한 cleanupname을 다시 사용하고, 다른 함수들과 조합해 봐요.

julia> firstletter("Jane")
"J"

3. 첫 글자를 이니셜로 만들기

initial 메서드를 구현해요. 이름을 받아서 첫 글자를 대문자로 만들고 뒤에 마침표를 붙여 반환해요. 이전 단계에서 정의한 firstletter를 다시 사용해 봐요.

initial("Robert")
"R."

4. 이니셜을 하트 안에 넣기

couple 메서드를 구현해요. 이름 두 개를 받아서 양쪽에 하트 이모지를 붙인 이니셜을 반환해요. 이전 단계에서 정의한 initial을 다시 사용해 봐요.

couple("Blake Miller", "Riley Lewis")
"❤ B.  +  R. ❤"
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Julia Exercism

고등학교 연인 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Julia 트랙을 개념 35개연습 문제 128개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.