Elixir 간단명료 노트 (15) - 제어 구조의 패턴 일치

10431 단어
프로 그래 밍 언어 에서 프로 세 스 제 어 는 중요 한 부분 이다.절 차 는 크게 순서, 조건 과 순환 구조 로 나 눌 수 있다.재 미 있 는 것 은 Elixir 이러한 구조의 키 워드 를 직접 제공 하지 않 고 모델 매 칭, 매 거 진 교체, 재 귀 를 통 해 절차 통 제 를 실현 하 는 것 이다.
패턴 매 칭
앞에서 간단 한 패턴 매 칭 을 소개 했다. 특히 서로 다른 데이터 구 조 를 소개 할 때 도 이 모듈 에 대해 패턴 매 칭 방식 을 제공한다.이 편 은 패턴 매 칭 에 관 한 토론 으로 패턴 매 칭 의 총화 와 같다.
기본 정의
패턴 일치 란 일치 문자 = 를 사용 하여 왼쪽 과 오른쪽 변 수 를 연결 하 는 것 입 니 다.왼쪽 표현 식 이나 변 수 는 패턴 (pattern) 이 라 고 부 르 고 오른쪽 표현 식 은 값 을 구 합 니 다.그 다음 에 오른쪽 에서 값 을 구 하 는 결 과 를 왼쪽 모드 와 일치 시 키 고 일치 하면 해당 하 는 변 수 를 연결 합 니 다.패턴 이 일치 하 는 표현 식 은 오른쪽 표현 식 의 값 을 구 하 는 결 과 를 되 돌려 줍 니 다.
Tuple 매 칭
모드 는 변수 일 수도 있 고 원 그룹 일 수도 있 습 니 다.elixir 의 모든 표현 식 은 값 을 되 돌려 줍 니 다. 패턴 이 일치 하면 일치 하 는 오른쪽 값 을 되 돌려 줍 니 다.
iex(1)> person = {"Bob", 25}            #     ,            ,           
{"Bob", 25}
iex(2)> {name, age} = {"Bob", 25}
{"Bob", 25}
iex(3)> name                            #      name
"Bob"
iex(4)> age                             #      age
25

Elixir 에 서 는 함수 가 되 돌아 오 는 여러 값 을 tuple 에 넣 고 tuple 의 pattern match 를 자주 사용 합 니 다.또한 끼 워 넣 을 수 있 습 니 다:
iex(1)> {date, time} = :calendar.local_time
{{2016, 4, 7}, {20, 49, 11}}
iex(2)> {year, month, day} = date
{2016, 4, 7}
iex(3)> year
2016
iex(4)> {{year, month, day}, {hour, minutes, second}} = :calendar.local_time
{{2016, 4, 7}, {20, 58, 3}}
iex(5)> year
2016
iex(6)> hour
20
iex(7)> {{year, month, day}, {hour, minutes, second}} = {date, time} = :calendar.local_time
{{2016, 4, 7}, {20, 59, 7}}
iex(8)> month
4
iex(9)> date
{2016, 4, 7}

마지막 표현 식 은 끼 워 넣 기 일치 입 니 다. 즉, 맨 오른쪽 에서 패턴 을 일치 시 킨 다음 에 맨 오른쪽 표현 식 의 값 을 패턴 과 일치 시 키 는 성공 적 인 결과 (: calendar. local time) 를 되 돌려 준 다음 에 이 결 과 는 왼쪽 {year, month, day}, {hour, minutes, second} 과 계속 패턴 을 일치 시 킵 니 다.
고정 정합
비록 elixir 의 데 이 터 는 변 하지 않 지만 변 수 는 다시 연결 할 수 있 습 니 다.변 수 를 다시 연결 할 필요 가 없 을 때 도 있 습 니 다. 이 때 는 pin 연산 자 (^) 를 사용 하여 고정 적 으로 일치 할 수 있 습 니 다.
iex(10)> expected_name = "Bob"
"Bob"
iex(11)> {^expected_name, _} = {"Bob", 25}     #      expected_name
{"Bob", 25}
iex(12)> {^expected_name, _} = {"Alice", 30}      
** (MatchError) no match of right hand side value: {"Alice", 30}


목록 일치
일치 하 는 목록 과 일치 하 는 원 그룹의 차 이 는 그리 크 지 않 습 니 다.목록 작업 head 와 tail 의 특수성 으로 인해 | 을 사용 하여 목록 과 일치 할 수 있 습 니 다. 바 인 딩 되 지 않 으 려 는 변 수 를 만나면 _ 를 사용 하여 모든 모드 와 일치 할 수 있 고 바 인 딩 되 지 않 음 을 표시 합 니 다.
iex(15)> [1, second, third] = [1, 2, 3]
[1, 2, 3]
iex(16)> second
2
iex(17)> [head|tail] = [1, 2, 3]
[1, 2, 3]
iex(18)> head
1
iex(19)> [1|tail] = [1, 2, 3]
[1, 2, 3]
iex(20)> tail
[2, 3]

일치 하 는 것 은 매우 유연 합 니 다. 같은 변 수 는 여러 번 일치 할 수 있 지만 여러 변수 와 일치 할 수 없습니다.
iex(21)> [first, first, first] = [1, 1, 1]
[1, 1, 1]
iex(22)> first
1
iex(23)> [^first, second, _] = [1, 2, 3]
[1, 2, 3]
iex(24)> first
1
iex(25)> second
2
iex(26)> [first|first] = [1, 1]
** (MatchError) no match of right hand side value: [1, 1]

iex(26)> [first, first] = [1, 2]
** (MatchError) no match of right hand side value: [1, 2]


맵 매 칭
맵 의 일치 와 목록 은 원본 그룹 과 다 릅 니 다. list 와 tuple 은 일치 하 는 요 소 를 모두 써 야 합 니 다. list 의 | 도 마찬가지 입 니 다.맵 은 일부 모드 만 일치 할 수 있 으 며, 일치 하지 않 으 면 오류 가 발생 합 니 다:
iex(26)> %{age: age} = %{name: "Bob", age: 25}
%{age: 25, name: "Bob"}
iex(27)> age
25
iex(28)> name
** (RuntimeError) undefined function: name/0
iex(28)> %{age: age, work_at: work_at} = %{name: "Bob", age: 25}
** (MatchError) no match of right hand side value: %{age: 25, name: "Bob"}


함수 매 칭
함수 의 매개 변 수 는 패턴 매 칭 을 할 수 있 습 니 다.같은 함수 이름, 서로 다른 매개 변수 모드 는 서로 다른 매개 변수 와 일치 할 수 있 습 니 다. 다 중 함수 논 리 를 실행 하고 일치 하지 않 으 면 이상 을 던 집 니 다.
iex(1)> defmodule Geometry do
...(1)>   def area({:rectangle, a, b}) do
...(1)>     a * b
...(1)>   end
...(1)>
...(1)>   def area({:square, a}) do
...(1)>     a * a
...(1)>   end
...(1)>
...(1)>   def area({:circle, r}) do
...(1)>     r * r * 3.14
...(1)>   end
...(1)> end
{:module, Geometry,
 <<70, 79, 82, 49, 0, 0, 5, 124, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 117, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 2, 104, 2, ...>>,
 {:area, 1}}
iex(2)> Geometry.area({:rectangle, 4, 5})
20
iex(3)> Geometry.area({:square, 5})
25
iex(4)> Geometry.area({:circle, 4})
50.24
iex(5)> Geometry.area({:triangle, 1, 2, 3})
** (FunctionClauseError) no function clause matching in Geometry.area/1
    iex:2: Geometry.area({:triangle, 1, 2, 3})

오류 가 발생 하지 않도록 오 류 를 처리 하 는 일치 함 수 를 쓸 수 있 습 니 다. 만능 으로 handler 오 류 를 일치 시 킬 수 있 습 니 다.모듈 에 넣 을 수 없 는 첫 번 째 함 수 를 정의 합 니 다. 함 수 는 순서대로 위 에서 아래로 순서대로 일치 하기 때문에 첫 번 째 에 쓰 면 뒤의 논리 함수 와 일치 하지 않 습 니 다.
iex(1)> defmodule Geometry do
...(1)>   def area({:rectangle, a, b}) do
...(1)>     a * b
...(1)>   end
...(1)>
...(1)>   def area({:square, a}) do
...(1)>     a * a
...(1)>   end
...(1)>
...(1)>   def area({:circle, r}) do
...(1)>     r * r * 3.14
...(1)>   end
...(1)>   def area(unknow) do
...(1)>     {:error, {:unknow_shape, unknow}}
...(1)>   end
...(1)> end

...(2)>Geometry.area({:triangle, 1, 2, 3})
{:error, {:unknown_shape, {:triangle, 1, 2, 3}}}

익명 함수 에서 우 리 는 & 를 사용 하여 함 수 를 인용 합 니 다. 함수 도 이러한 문법 사탕 을 써 서 일치 할 수 있 습 니 다.
iex(5)> fun = &Geometry.area/1
&Geometry.area/1
iex(6)> fun.({:circle, 4})
50.24
iex(7)> fun.({:square, 5})
25

Guards 위 자구
매개 변수 가 패턴 매 칭 을 하 는 것 을 제외 하고 함수 가 guards 문 구 를 제공 할 수 있 습 니 다.guard 문 구 를 통 해 매개 변 수 를 걸 러 냅 니 다.
iex(8)> defmodule TestNum do
...(8)>   def test(x) when x < 0 do
...(8)>     :negative
...(8)>   end
...(8)>
...(8)>   def test(0) , do: :zero
...(8)>
...(8)>   def test(x) when x > 0 do
...(8)>
...(8)>     :positive
...(8)>   end
...(8)>
...(8)> end
{:module, TestNum,
 <<70, 79, 82, 49, 0, 0, 4, 184, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 111, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 2, 104, 2, ...>>,
 {:test, 1}}
iex(9)> TestNum.test(-1)
:negative
iex(10)> TestNum.test(0)
:zero
iex(11)> TestNum.test(1)
:positive
iex(12)> TestNum.test(:not_a_number)
:positive

마지막 일치 도 값 을 되 돌려 줍 니 다.Elixir 에서 데이터 형식 은 모두 비교 문자 >< 를 통 해 조작 할 수 있 는데 그 우선 순 위 는 다음 과 같다.number < atom < reference < fun < port < pid < tuple < map < list < bitstring (binary)
비 숫자 를 걸 러 내기 위해 서 는 guards 를 다음 과 같이 수정 할 수 있 습 니 다.
iex(15)> defmodule TestNum do
...(15)>   def test(x) when is_number(x) and x < 0 do
...(15)>     :negative
...(15)>   end
...(15)>   def test(0), do: :zero
...(15)>   def test(x) when is_number(x) and x > 0 do
...(15)>     :positive
...(15)> end end
iex:15: warning: redefining module TestNum
{:module, TestNum,
 <<70, 79, 82, 49, 0, 0, 4, 248, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 111, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 2, 104, 2, ...>>,
 {:test, 1}}
iex(16)> TestNum.test(-1)
:negative
iex(17)> TestNum.test(:not_a_number)
** (FunctionClauseError) no function clause matching in TestNum.test/1
    iex:16: TestNum.test(:not_a_number)

guards 문 구 를 쓸 때 주의해 야 합 니 다. 함 수 를 호출 하면 오류 가 발생 할 수 있 습 니 다.그러나 guard 문 구 를 쓴 후에 오 류 는 숨겨 지고 던 지지 않 습 니 다. gurad 문 구 는 false 로 돌아 갑 니 다.예 를 들 어 length / 1 함 수 는 list 에 만 길 이 를 구 합 니 다.
iex(25)> defmodule ListHelper do
...(25)>   def smallest(list) when length(list) > 0 do
...(25)>     Enum.min(list)
...(25)>   end
...(25)>   def smallest(_), do: {:error, :invalid_argument}
...(25)> end
iex:25: warning: redefining module ListHelper
{:module, ListHelper,
 <<70, 79, 82, 49, 0, 0, 5, 56, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 118, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 2, 104, 2, ...>>,
 {:smallest, 1}}
iex(26)> length [1, 2, 3]
3
iex(27)> length {1, 2, 3}               #  tuple    
** (ArgumentError) argument error
    :erlang.length({1, 2, 3})
iex(27)> length 123                     #        
** (ArgumentError) argument error
    :erlang.length(123)
iex(27)> ListHelper.smallest([1, 2, 3])
1
iex(28)> ListHelper.smallest(123)       #       ,    
{:error, :invalid_argument}
iex(29)> ListHelper.smallest({1, 2, 3}) #       ,    
{:error, :invalid_argument}
iex(30)> ListHelper.smallest(1, 2, 3)       
** (UndefinedFunctionError) undefined function: ListHelper.smallest/3
    ListHelper.smallest(1, 2, 3)

마지막 예 는 매우 재 미 있 습 니 다. 함수 매개 변 수 는 패턴 매 칭 을 할 수 있 지만 매개 변수 서명 과 같은 함 수 를 말 합 니 다.마지막 예 가 잘못 되 었 고 일치 하 는 오류 가 없습니다. ListHelper. smallest / 1 은 하나의 인 자 를 표시 하고 ListHelper. smallest / 3 은 세 개의 인 자 를 표시 합 니 다.모듈 은 ListHelper. smallest / 1 의 오류 일치 만 정 의 했 고, ListHelper. smallest / 3 은 없 었 기 때문에 일치 실패 로 오 류 를 던 졌 습 니 다.
lambdas 일치
명명 함 수 는 여러 함수 서명 을 정의 하여 다 중 모드 매 칭 을 사용 할 수 있 습 니 다. 익명 함 수 는 여러 def 정 의 를 쓸 수 없 지만 다 중 매개 변 수 를 사용 하여 패턴 매 칭 을 할 수 있 습 니 다. 쓰기 도 간단 하고 guards 문 구 를 지원 합 니 다.
기본 적 인 형식 은:
fn
  pattern_1 -> 
    ...             Executed if pattern_1 matches 
    ... 
  end

  pattern_2 -> 
    ...             Executed if pattern_2 matches
    ... 
  end
iex(30)> test_num = fn
...(30)>   x when is_number(x) and x < 0 ->
...(30)>     :negative
...(30)>   0 -> :zero
...(30)>   x when is_number(x) and x > 0 ->
...(30)>     :positive
...(30)> end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex(31)> test_num.(-1)


패턴 매 칭 을 통 해 많은 제어 구 조 를 실현 할 수 있 습 니 다.함수 에 맞 는 guard 자 구 는 if 조건 문 이 필요 하지 않 습 니 다.물론 Elixir 는 if 조건문 이 없 지만 if 매크로 를 제공 합 니 다.그 역할 은 if 조건 문 과 유사 하 며, 매크로 에 깊이 들 어가 기 전에 일단 하나의 일 로 여 긴 다.다음 절 에 서 는 Elixir 의 조건 제어 방식 을 소개 할 것 입 니 다.

좋은 웹페이지 즐겨찾기