5 case,cond 와 if - case, cond and if

이 장에서는 case , cond 그리고 if 같은 제어 구조에 대해 알아 보겠습니다.

In this chapter, we will learn about the case, cond and if control-flow structures.

5.1 case - case

case 는 어느 한곳에 매칭 될 때까지 여러 패턴과 비교해 줍니다:

case allows us to compare a value against many patterns until we find a matching one:

iex> case {1, 2, 3} do
...>   {4, 5, 6} ->
...>     "This clause won't match"
...>   {1, x, 3} ->
...>     "This clause will match and bind x to 2 in this clause"
...>   _ ->
...>     "This clause would match any value"
...> end

만약 이미있는 변수와 패턴 매칭시키고 싶은 경우 ^ 를 사용해야합니다:

If you want to pattern match against an existing variable, you need to use the ^ operator:

iex> x = 1
1
iex> case 10 do
...>   ^x -> "Won't match"
...>   _  -> "Will match"
...> end

절은 guards를 통해 추가 조건을 명시 할 수 있습니다:

Clauses also allow extra conditions to be specified via guards:

iex> case {1, 2, 3} do
...>   {1, x, 3} when x > 0 ->
...>     "Will match"
...>   _ ->
...>     "Won't match"
...> end

첫 번째 절은 x 가 양수일 경우에만 매치합니다..

The first clause above will only match when x is positive.

5.2 guard 절 표현식 - Expressions in guard clauses.

Erlang VM은 guards에서 사용할 표현식에 제한을 둡니다:

The Erlang VM only allows a limited set of expressions in guards:

  • 비교 연산자 (==!====!==><<=>=)
  • 논리연산자 (andor) 와 부정 연산자(not!)
  • 산술 연산자 (+-*/)
  • 왼쪽이 리터럴의 경우 <>++
  • in 연산자
  • 다음의 모든 유형 검사 함수들:

    • is_atom/1
    • is_binary/1
    • is_bitstring/1
    • is_boolean/1
    • is_float/1
    • is_function/1
    • is_function/2
    • is_integer/1
    • is_list/1
    • is_map/1
    • is_number/1
    • is_pid/1
    • is_port/1
    • is_reference/1
    • is_tuple/1
  • 추가적으로 아래 함수들:

    • abs(number)
    • bit_size(bitstring)
    • byte_size(bitstring)
    • div(integer, integer)
    • elem(tuple, n)
    • hd(list)
    • length(list)
    • map_size(map)
    • node()
    • node(pid | ref | port)
    • rem(integer, integer)
    • round(number)
    • self()
    • tl(list)
    • trunc(number)
    • tuple_size(tuple)
  • comparison operators (==, !=, ===, !==, >, <, <=, >=)

  • boolean operators (and, or) and negation operators (not, !)

  • arithmetic operators (+, -, *, /)

  • <> and ++ as long as the left side is a literal

  • the in operator

  • all the following type check functions:

    • is_atom/1
    • is_binary/1
    • is_bitstring/1
    • is_boolean/1
    • is_float/1
    • is_function/1
    • is_function/2
    • is_integer/1
    • is_list/1
    • is_map/1
    • is_number/1
    • is_pid/1
    • is_port/1
    • is_reference/1
    • is_tuple/1
  • plus these functions:

    • abs(number)
    • bit_size(bitstring)
    • byte_size(bitstring)
    • div(integer, integer)
    • elem(tuple, n)
    • hd(list)
    • length(list)
    • map_size(map)
    • node()
    • node(pid | ref | port)
    • rem(integer, integer)
    • round(number)
    • self()
    • tl(list)
    • trunc(number)
    • tuple_size(tuple)

guards 에서 발생하는 에러들은 단순히 guard의 실패로 간주되며 guard 밖에 영향을 주지 않습니다:

Keep in mind errors in guards do not leak but simply make the guard fail:

iex> hd(1)
** (ArgumentError) argument error
    :erlang.hd(1)
iex> case 1 do
...>   x when hd(x) -> "Won't match"
...>   x -> "Got: #{x}"
...> end
"Got 1"

만약 어떤 절도 일치하지 않으면 오류가 발생합니다:

If none of the clauses match, an error is raised:

iex> case :ok do
...>   :error -> "Won't match"
...> end
** (CaseClauseError) no case clause matching: :ok

익명 함수도 절과 guards를 여러 개 가질수 있다는 점에 유의하십시요:

Note anonymous functions can also have multiple clauses and guards:

iex> f = fn
...>   x, y when x > 0 -> x + y
...>   x, y -> x * y
...> end
#Function<12.71889879/2 in :erl_eval.expr/5>
iex> f.(1, 3)
4
iex> f.(-1, 3)
-3

익명 함수 절에서는 인자의 수가 동일해야합니다, 그렇지 않으면 오류가 발생합니다.

The number of arguments in each anonymous function clause needs to be the same, otherwise an error is raised.

5.3 cond - cond

case 는 서로 다른 값에 대해 일치 시키려고 할 때 유용합니다. 그러나 경우에 따라서는 먼저 평가한 식이 true 가 될 때까지 여러 조건을 확인하길 원합니다. 그런 경우 cond 를 사용할 것입니다:

case is useful when you need to match against different values. However, in many circumstances, we want to check different conditions and find the first one that evaluates to true. In such cases, one may use cond:

iex> cond do
...>   2 + 2 == 5 ->
...>     "This will not be true"
...>   2 * 2 == 3 ->
...>     "Nor this"
...>   1 + 1 == 2 ->
...>     "But this will"
...> end
"But this will"

이것은 명령형 언어의 else if 절과 동일합니다 (비록 사용할 기회는 적지만).

This is equivalent to else if clauses in many imperative languages (although used way less frequently here).

만약 true 가 되는 조건이 없으면 오류가 발생합니다. 따라서 항상 매칭이 될 수 있는 true 조건을 추가할 필요가 있을 수 있습니다:

If none of the conditions return true, an error is raised. For this reason, it may be necessary to add a final condition, equal to true, which will always match:

iex> cond do
...>   2 + 2 == 5 ->
...>     "This is never true"
...>   2 * 2 == 3 ->
...>     "Nor this"
...>   true ->
...>     "This is always true (equivalent to else)"
...> end

마지막으로 condnilfalse 이외의 어떤 값도 true 로 고려한다는것에 주의하십시요:

Finally, note cond considers any value besides nil and false to be true:

iex> cond do
...>   hd([1,2,3]) ->
...>     "1 is considered as true"
...> end
"1 is considered as true"

5.4 if 와 unless - if and unless

casecond 와 달리 한가지 조건을 확인하고 싶은 경우에 편리한 if/2unless/2 매크로도 제공합니다.

Besides case and cond, Elixir also provides the macros if/2 and unless/2 which are useful when you need to check for just one condition:

iex> if true do
...>   "This works!"
...> end
"This works!"
iex> unless true do
...>   "This will never be seen"
...> end
nil

만약 if/2false 또는 nil 을 리턴하면 do/end 로 묶인 부분은 실행되지 않고 단순히 nil 이 리턴됩니다. 그 반대가 되는 것이 unless/2 입니다.

If the condition given to if/2 returns false or nil, the body given between do/end is not executed and it simply returns nil. The opposite happens with unless/2.

else 블록도 사용할 수 있습니다:

They also support else blocks:

iex> if nil do
...>   "This won't be seen"
...> else
...>   "This will"
...> end
"This will"

참고 : 언어에서 if/2unless/2 가 매크로로 구현되는 것이 재미있는 점입니다; 이들은 다른 다양한 언어들이 그렇듯이 특별한 언어 구조가 없습니다. Kernel 모듈 문서에서 if/2 의 문서와 소스를 확인할 수 있습니다. 모듈 Kernel 에는 연산자 +/2 와 함수 is_function/2 와 같이 코드를 작성할 때 기본적으로 자동 import되어 사용할 수 있는것들이 정의되어 있습니다.

Note: An interesting note regarding if/2 and unless/2 is that they are implemented as macros in the language; they aren't special language constructs as they would be in many languages. You can check the documentation and the source of if/2 in the Kernel module docs. The Kernel module is also where operators like +/2 and functions like is_function/2 are defined, all automatically imported and available in your code by default.

5.5 do 블록 - do blocks

지금까지 우리는 4 개의 제어 구조 case , cond , if 그리고 unless 를 배웠습니다: 그리고 이것들은 모두 do/end 블록에 둘러싸여 있었습니다. 사실 if 를 다음과 같이 쓸 수도 있습니다:

At this point, we have learned four control structures: case, cond, if and unless, and they were all wrapped in do/end blocks. It happens we could also write if as follows:

iex> if true, do: 1 + 2
3

Elixir에서 do/end 블록은 표현식들을 쉽게 do:에 전달하기 위한 것입니다. 이것들은 동일합니다:

In Elixir, do/end blocks are a convenience for passing a group of expressions to do:. These are equivalent:

iex> if true do
...>   a = 1 + 2
...>   a + 10
...> end
13
iex> if true, do: (
...>   a = 1 + 2
...>   a + 10
...> )
13

두 번째 구문은 키워드 리스트를 사용하고 있다고 말합니다. else 같은 구문으로 넘길 수 있습니다:

We say the second syntax is using keyword lists. We can pass else using this syntax:

iex> if false, do: :this, else: :that
:that

do/end 블록을 사용시에는 한가지 명심할 점이 있습니다. 블록은 항상 가장 바깥쪽 함수 호출에 바인딩 됩니다. 예를 들어, 아래식은:

One thing to keep in mind when using do/end blocks is they are always bound to the outermost function call. For example, the following expression:

iex> is_number if true do
...>  1 + 2
...> end

이렇게 파싱됩니다:

Would be parsed as:

iex> is_number(if true) do
...>  1 + 2
...> end

그러면 Elixir는 is_number/2 를 호출하려고 하고 정의되지 않은 함수 오류를 발생시킬것입니다. 모호성을 해소하려면 명시적으로 괄호를 추가하면 좋습니다:

Which leads to an undefined function error as Elixir attempts to invoke is_number/2. Adding explicit parentheses is enough to resolve the ambiguity:

iex> is_number(if true do
...>  1 + 2
...> end)
true

키워드 리스트는 언어에서 매우 중요한 역할을 담당하고 있으며, 다양한 함수와 매크로에서 아주 흔합니다. 이후 장에서는 이 점을 좀 더 자세히 살펴 볼것입니다. 지금은 "바이너리, 문자열 그리고 문자 리스트"에 대해 이야기하겠습니다.

Keyword lists play an important role in the language and are quite common in many functions and macros. We will explore them a bit more in a future chapter. Now it is time to talk about "Binaries, strings and char lists".