블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2023. 1. 25. 14:57 Elixir/Basic

OS 별 Elixir 설치

https://elixir-lang.org/install.html

// MacOS
brew install elixir

brew로 설치하며 brew 에러가 발생하여 다음 두가지 명령을 통해 brew 업데이트가 필요하다. 시간이 10분가량 소요된다.

// To `brew update`, first run:
  git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
  git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow
  
// 완료시
  brew update
  
// 재 실행
  brew install elixir
// 설치 dependencies
==> Fetching dependencies for elixir: ca-certificates, openssl@1.1, m4, libtool, unixodbc, jpeg-turbo, libpng, lz4, xz, zstd, libtiff, pcre2, wxwidgets and erlang
==> Fetching ca-certificates

// 정상 설치 확인
  elixir -v
Erlang/OTP 25 [erts-13.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns] [dtrace]
Elixir 1.14.3 (compiled with Erlang/OTP 25)

Erlang VM 위에서 Elixir가 수행되기에 Erlang/OTP (Open Telecom Platform)과 Elixir 버전이 같이 나오는 것 같다. 

 

 

IEX를 통한 연산자 실습

Interactive EliXir 를 통해 기본 실습을 한다. 

$ iex
Erlang/OTP 25 [erts-13.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns] [dtrace]
Interactive Elixir (1.14.3) - press Ctrl+C to exit (type h() ENTER for help)

- Integer, Float, Boolean

- Atom(애텀): 문자가 값이 되는 상수, 앞에 :를 붙인다. Boolean도 atom이고, Module명도 atom이다.

iex> :test
:test
iex> :true
true
iex(11)> is_atom(MyApp.Module)
true
iex(12)> is_atom(MyApp)
true
iex(14)> is_atom(true)
true

- String: UTF-8

- 수치 연산: + - * / 지원, 

- 논리 연산: && || !  (and or not은 첫번째 인자가 boolean일 경우 사용 가능)

iex(15)> true and false
false
iex(16)> true or false
true

- 비교 연산: == != === <= >= < >  (정수 실수 비교는 ===)

iex(17)> 2 == 2.0
true
iex(18)> 2 === 2.0
false

타입에 따른 비교연산을 수행할 수도 있다. 

iex(19)> :hi > 999999
true

- Text interpolation: " #{value}"

iex(20)> name = "hi"
"hi"
iex(21)> "#{name} elixir"
"hi elixir"

- Text concatenation: <> 

iex(22)> hello = "hello"
"hello"
iex(23)> hello <> "elixir"
"helloelixir"

 

Collection 실습

- 종류: 리스트, 튜플, 키워드 리스트, 맵

- 리스트: value collection, | 로 연결시 0(n) 선형복잡도를 갖는다. 이런 이유로 리스트는 추가하는 것을 앞에 두는게 뒤보다 빠르다. linked list로 관리하고, 요소가 늘었다 줄었다 할 수 있고, 추가시에는 앞에 넣는게 속도면에서 낳다

// 여러 타입
iex(24)> [3, "hi", "dowon"]
[3, "hi", "dowon"]
iex(25)> list = [ 3, :hi, "peter"]
[3, :hi, "peter"]
// 합치기
iex(26)> ["test" | list]
["test", 3, :hi, "peter"]

- ++ : 좌 우 더하기

- -- : 오른쪽 모든 요소에 대해 왼쪽에서 처음 만난 요소만 지움, (it's safe to subtract a missin value)

iex(27)> [1, 2] ++ [ 3,4]
[1, 2, 3, 4]
iex(28)> [1,2]++[1,2]
[1, 2, 1, 2]
iex(29)> [1,2]--[1,2]
[]
iex(30)> [1,2,3]--[2,4]
[1, 3]
iex(31)> [1,2,3,4,5] -- [1,3,4]

- hd: head는 첫번째 요소 하나

- tl: tail은 head 첫번째 뺀 나머지

iex(32)> hd [1,2,3,4,"5"]
1
iex(33)> tl [1,2,3,4,"5"]
[2, 3, 4, "5"]
iex(34)> [h | t]=[1,2,3,4,5,"6"]
[1, 2, 3, 4, 5, "6"]
iex(35)> h
1
iex(36)> t
[2, 3, 4, 5, "6"]

- 튜플(Tuple): 메모리에 연속적으로 저장됨. 길이 구하는 것은 빠르나 수정은 비용이 비싸다. 즉, 추가하는 데이터가 아니라면 메모리 블락으로 움직이니 경우 사용하면 좋다. 함수의 추가정보 반환에 쓰임

iex(37)> {1,2,3, :hi, "peter"}
{1, 2, 3, :hi, "peter"}

- 키워드 리스트(Keyword list): atom을 key로 튜플의 리스트와 같다. 함수의 옵션 전달에 사용한다. (애텀은 :<name>형식이다), 애텀을 해쉬 테이블로 관리한다. 

  + 모든 키는 Atom이다

  + 키는 정렬되어 있다

  + 키는 유니크하지 않다

iex(38)> [say: "hi", name: "peter"]
[say: "hi", name: "peter"]
iex(39)> [{:say, "hi"}, {:name, "peter"}]
[say: "hi", name: "peter"]

 - 맵(Map): %{} 문법, keyword list와 틀리게 어떤 type의 key든 허용하고, 순서를 따르지 않는다.

   + %{atom => value} ---> %{key: value}

iex(40)> map = %{:say => "hi", :name => 4}
%{name: 4, say: "hi"}
iex(41)> map[:name]
4
iex(42)> map2 = %{"hi" => "hello"}
%{"hi" => "hello"}
iex(43)> map2["hi"]
"hello"
iex(44)> %{say: "hi", name: "peter"}
%{name: "peter", say: "hi"}
// 비교
iex(45)> %{hi: "yo"} === %{:hi => "yo"}
true
iex(46)> %{hi: "yo"} == %{:hi => "yo"}
true
iex(47)> map = %{hi: "yo"}
%{hi: "yo"}
iex(48)> map.hi
"yo"
// 같은 key는 뒤에 것으로 대체
iex(49)> %{hi: "yo", hi: "hello"}
warning: key :hi will be overridden in map
  iex:49

%{hi: "hello"}

 - | 를 통한 갱신, 새로운 맵을 생성하는 것이다. 이것은 추가가 아닌 기존 애텀 key가 매칭된 값을 업데이트 한다. 

iex(51)> map = %{ hi: "hello" }
%{hi: "hello"}
iex(52)> %{ map | name: "peter"}
** (KeyError) key :name not found in: %{hi: "hello"}
    (stdlib 4.2) :maps.update(:name, "peter", %{hi: "hello"})
    (stdlib 4.2) erl_eval.erl:309: anonymous fn/2 in :erl_eval.expr/6
    (stdlib 4.2) lists.erl:1350: :lists.foldl/3
    (stdlib 4.2) erl_eval.erl:306: :erl_eval.expr/6
    (elixir 1.14.3) src/elixir.erl:294: :elixir.eval_forms/4
    (elixir 1.14.3) lib/module/parallel_checker.ex:110: Module.ParallelChecker.verify/1
    (iex 1.14.3) lib/iex/evaluator.ex:329: IEx.Evaluator.eval_and_inspect/3
    (iex 1.14.3) lib/iex/evaluator.ex:303: IEx.Evaluator.eval_and_inspect_parsed/3
iex(52)> %{ map | hi: "yo"}
%{hi: "yo"}

 

 

Enum 실습

Enum (enumerable, 열거형) 모듈은 70 가량의 함수를 가지고 있고, 열거형에 작동한다. 튜플은 제외

- all?: Enum.all?(collection, function) 모든 요소가 true 이어야 true 이다.

- any?: Enum.any?(collection, function) 하나 요소라도 true 이면 true 이다.

iex(54)> Enum.all?(["hi", "peter"], fn(s) -> String.length(s) == 2 end)
false
iex(55)> Enum.all?(["hi", "peter"], fn(s) -> String.length(s) >= 2 end)
true
iex(56)> Enum.any?(["hi", "peter"], fn(s) -> String.length(s) == 2 end)
true

- chunk_every(collection, count): count 만큼씩 나눔

- chunk_by(collection, function): function 반환되는 결과값이 변할때 마다 나눔

- max_every(collection, count, function): count 만큼 묶고, 첫번째에 함수 반환값으로 대체한다.

iex(57)> Enum.chunk_every([1,2,3,4,5,6], 2)
[[1, 2], [3, 4], [5, 6]]
iex(58)> Enum.chunk_by(["hi", "yo", "yun", "do", "dowon"], fn(s) -> String.length(s) end)
[["hi", "yo"], ["yun"], ["do"], ["dowon"]]
iex(59)> Enum.map_every([1,2,3,4,5,6,7], 2, fn(s) -> s + 1000 end)
[1001, 2, 1003, 4, 1005, 6, 1007]

- each: 새로운 값을 만들지 않고 열거 하고 싶을 경우 사용한다

- map: 새로운 값을 생성하여 collection을 반환한다

- min, max: 최소, 최대값 반환 

- filter: true인 것만 반환

- reduce: 하나의 값으로 추려줌, function을 통해 선택적으로 추릴 수도 있음

- sort: 정렬 순서로 Erlang의 텀(Term)순서를 사용한다. function을 통한 정렬도 가능하다.

iex(60)> Enum.each([1,2,3,4], fn(s) -> IO.puts(s) end)
1
2
3
4
:ok
iex(61)> Enum.map([1,2,3,4], fn(s) -> s + 10 end)
[11, 12, 13, 14]
iex(62)> Enum.min([1,2,3,4])
1
iex(63)> Enum.max([1,2,3,4])
4
iex(64)> Enum.filter([1,2,3,4], fn(s) -> s/2 == 0 end)
[]
iex(65)> Enum.filter([1,2,3,4], fn(s) -> s/2 === 0 end)
[]
iex(66)> Enum.filter([1,2,3,4], fn(s) -> rem(s, 2) == 0 end)
[2, 4]
iex(67)> Enum.reduce([1,2,3,4], fn(s, acc) -> s+acc end)
10
iex(68)> Enum.sort([5,6,7,4,1])
[1, 4, 5, 6, 7]
iex(69)> Enum.sort([5,6,7,Enum, :foo, 4,"hi", 1])
[1, 4, 5, 6, 7, Enum, :foo, "hi"]
iex(70)> Enum.sort([5,6,7,Enum, :foo, 4,"hi", 1], :asc)
[1, 4, 5, 6, 7, Enum, :foo, "hi"]
iex(71)> Enum.sort([5,6,7,Enum, :foo, 4,"hi", 1], :desc)
["hi", :foo, Enum, 7, 6, 5, 4, 1]

- uniq: 중복 제거한 collection 반환 

- uniq_by: function을 통해 중복되는 것 제거한 collection 반환 

iex(72)> Enum.uniq([1,2,3,2,3,4])
[1, 2, 3, 4]
iex(74)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 2}], fn(s) -> s.y end)
[%{x: 1, y: 1}, %{x: 3, y: 2}]

- Capture operator(&): 익명함수를 간결하게 표현한다, & 는 익명함수로 (내용) 괄호로 감쌈. 변수 &1 인 전달 요소 할당한다. 익명함수를 변수에 할당하여 사용도 가능

iex(74)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 2}], fn(s) -> s.y end)
[%{x: 1, y: 1}, %{x: 3, y: 2}]
iex(75)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 2}], &(&1.y))
[%{x: 1, y: 1}, %{x: 3, y: 2}]
// 변수에 할당
iex(76)> check = &(&1.y)
#Function<42.3316493/1 in :erl_eval.expr/6>
iex(77)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 2}], check)
[%{x: 1, y: 1}, %{x: 3, y: 2}]

- 함수에 이름할당하여 사용

// First 모듈에 check 함수 만들기
iex(78)> defmodule First do
...(78)>   def check(s), do: s.y
...(78)> end
{:module, First,
 <<70, 79, 82, 49, 0, 0, 5, 204, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 195,
   0, 0, 0, 22, 12, 69, 108, 105, 120, 105, 114, 46, 70, 105, 114, 115, 116, 8,
   95, 95, 105, 110, 102, 111, 95, 95, 10, ...>>, {:check, 1}}

// 호출 에러 사례
iex(79)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 2}], First.check)
** (UndefinedFunctionError) function First.check/0 is undefined or private. Did you mean:

      * check/1

    First.check()
    iex:79: (file)
iex(79)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 2}], First.check(s))
warning: variable "s" does not exist and is being expanded to "s()", please use parentheses to remove the ambiguity or change the variable name
  iex:79

** (CompileError) iex:79: undefined function s/0 (there is no such import)
    (elixir 1.14.3) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
    (elixir 1.14.3) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
    (elixir 1.14.3) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
    (elixir 1.14.3) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
    (elixir 1.14.3) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
    (elixir 1.14.3) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
    (elixir 1.14.3) src/elixir.erl:376: :elixir.quoted_to_erl/4
    
// 익명함수로 호출
iex(79)> Enum.uniq_by([%{x: 1, y: 1}, %{x: 2, y: 1}, %{x: 3, y: 2}], &(First.check(&1)))
[%{x: 1, y: 1}, %{x: 3, y: 2}]

 

 

<참조>

데이터 형에 대한 이행: https://www.bitzflex.com/6

 

Elixir 의 데이터 형(Type)

사족일 수 있지만, 컴퓨터는 사실 모든 정보를 숫자로 저장, 연산 처리를 합니다. MP3, JPG 이미지, 워드 문서 등등 컴퓨터가 처리하는 모든 정보 내용은 컴퓨터 내에서 수치화되어서 처리가 됩니

www.bitzflex.com

애텀과 변수의 이해: https://www.bitzflex.com/8

 

변수와 애텀(Atom)

Elixir를 배우기 시작하면서 가장 혼동이 오는 부분이 변수와 애텀이었습니다. 애텀은 ? 거의 모든 언어에서 부울린값으로 true, false 를 사용합니다. 그냥 0, 1 을 true, false의 의미로 사용할 수도 있

www.bitzflex.com

https://namu.wiki/w/Erlang

 

Erlang - 나무위키

병행성 프로그래밍 언어인 Erlang은 가벼운 프로세스를 아주 빠르게 생성한다. 각각의 프로세스들은 메시지 패싱에 의해 작업을 지시받고 결과를 출력하며 ETS, DETS 메모리 영역을 제외하면 공유

namu.wiki

https://elixirschool.com/ko/lessons/basics/basics

 

기본 · Elixir School

Elixir를 시작합시다. 기본적인 타입과 연산자를 배워봅시다. elixir-lang.org 홈페이지의 Installing Elixir 가이드에서 운영체제별로 설치하는 방법을 알아볼 수 있습니다. Elixir를 설치하고 나서 어떤 버

elixirschool.com

0(n) 선형 복잡도: https://hanamon.kr/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-time-complexity-%EC%8B%9C%EA%B0%84-%EB%B3%B5%EC%9E%A1%EB%8F%84/

 

[알고리즘] Time Complexity (시간 복잡도) - 하나몬

⚡️ Time Complexity (시간 복잡도) Time Complexity (시간 복잡도)를 고려한 효율적인 알고리즘 구현 방법에 대한 고민과 Big-O 표기법을 이용해 시간 복잡도를 나타내는 방법에 대해 알아봅시다. ❗️효

hanamon.kr

- Erlang Term 비교: https://www.erlang.org/doc/reference_manual/expressions.html#term-comparisons 

 

Erlang -- Expressions

maybe is an experimental new feature introduced in OTP 25. By default, it is disabled. To enable maybe, use compiler option {feature,maybe_expr,enable}. The feature must also be enabled in the runtime using the -enable-feature option to erl.

www.erlang.org

 

posted by 윤영식