-
[Haskell] #5 Type (2)๊ณต๋ถ/ํจ์ํ ํ๋ก๊ทธ๋๋ฐ 2020. 1. 20. 21:45
์ด์ ์ ํฌ๋ Haskell์์์ Type์ด๋, ํจ์ Type ์ ์ธ๊ณผ, ์ ๋ค๋ฆญ ์ฒ๋ผ ํจ์์ ํ๋ผ๋ฏธํฐ์ ๋ค์ํ ํ์ ์ ํ์ฉํ๋ ํ์ ๋ณ์, ๊ทธ๋ฆฌ๊ณ ์ผ๋ฐ์ ์ธ Type๋ค์ ๋ํด์ ๋ค๋ค์ด์.
2020/01/19 - [๊ณต๋ถ/ํจ์ํ ํ๋ก๊ทธ๋๋ฐ] - [Haskell] #4 Type (1)
์ค๋์ Type Class์ Haskell์์ ๋ง์ด ์ฐ๋ Type Class๋ฅผ ์ ๋ฆฌํด๋ณด๋ ค ํฉ๋๋ค.
- GHCi ์์ :t ๋ก ํ์ ์์๋ณด๊ธฐ
- ํจ์ Type์ ์ ์ธํ๊ธฐ
- ํ์ ๋ณ์. (ํ์ค์ผ ๊ณ์ ์ ๋ค๋ฆญ ์ฌ์ฉ๋ฒ?)
- ํ์ค์ผ์ ์ผ๋ฐ์ ์ธ ํ์ ๋ค
- Type Class
- ํ์ค์ผ์์ ๋ง์ด ์ฐ๋ Type Class ๋ค
- ๋ง๋ฌด๋ฆฌ
5. Type Class
ํ์ ํด๋์ค๋ ์ด๋ค ๋์์ ์ ์ํ๋ ์ธํฐํ์ด์ค์ ๋๋ค.
๊ฐ์ฒด ์งํฅ์์ ๋งํ๋ Class์๋ ๋ค๋ฆ ๋๋ค. (?? ์์ง์ ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.)
1. Type Class๋ ๋์๋ง ๋ช ์ํฉ๋๋ค. ์์ฑ์ด ์์ต๋๋ค.
2. ๋ค์ค์ Type Class์ Instance๊ฐ ๋๋ ๊ฒ์ด ๊ฐ๋ฅํฉ๋๋ค. ํ๋์ ํ์ ์ด ์ฌ๋ฌ ๊ฐ์ Type Class๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ซ์๋ฅผ ๋ํ๋ด๋ ํ์ ์ค ํ๋์ธ Integer๋ == ์ฐ์ฐ๋ ๋๊ณ , <= ์ฐ์ฐ๋ ๋ฉ๋๋ค. ์ด ๋์ ๊ฐ๊ฐ Eq, Ord ๋ผ๋ Type Class๋ฅผ ๊ฐ๊ณ ์์ต๋๋ค. ๋ฐ๋ผ์ Integer๋ 2๊ฐ์ Type Class๋ฅผ ๊ตฌํํ๋ Instance์ ๋๋ค.
3. Type Class๋ ๋ค๋ฅธ Type Class์ Instance๊ฐ ๋๊ธฐ ์ํ ์ ํ ์กฐ๊ฑด์ด ๋ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ๋น๊ต๋ฅผ ์ํ Type Class์ธ Ord์ <=๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์๋ ==(Eq)์ ๊ตฌํ์ด ๋ฐ๋์ ํ์ํฉ๋๋ค. ๋ฐ๋ผ์ Ord๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์๋ Eq๋ฅผ ๋ฐ๋์ ๊ตฌํํด์ผ ํฉ๋๋ค.
(ํฌ๊ฒ ๋ค๋ฆ ๋๋ค๋ง ์์ง๊น์ง๋ ๊ฐ์ด ์ ์ค์ง ์์ต๋๋ค. Java์ ์ธํฐํ์ด์ค ๋ณด๋ค๋, Swift์ ObjectiveC์ protocol ๊ฐ๋ ์ ๊ฐ๊น์ต๋๋ค.)
์ด๋ค ํจ์๊ฐ ์ด๋ค Type Class๋ฅผ ๋ฐ๋ฅธ๋ค๋ฉด, ๊ทธ ํจ์๋ Type Class์์ ์ง์ ํ ๋์์ ๊ตฌํํด์ผ ํฉ๋๋ค.
Type Class์ ์๋ก ==์ Type Signature๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
:t (==) // ==, /=์ ๊ฐ์ ์ค์ ํจ์๋ฅผ ์ ์ ํจ์์ฒ๋ผ ์ฐ๊ฑฐ๋ ๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ์ฃผ๋ ค๋ฉด ()๋ก ๊ฐ์ธ์ฃผ์ด์ผ ํฉ๋๋ค.
(==) :: Eq a => a -> a -> Bool์ ์๊ทธ๋์ฒ๋ฅผ ๋ณด๋ฉด == ์ฐ์ฐ์๋ ํจ์์ด๊ณ , ๊ฐ์ ํ์ (ํ์ ๋ณ์ a)๋ฅผ 2๊ฐ๋ฅผ ๋ฐ์, Bool์ ๋ฐํํฉ๋๋ค.
๊ทธ๋ฐ๋ฐ ๊ทธ ์์ Eq a => ๋ผ๋ ์๋ก์ด ๋ฌธ์๊ฐ ์์ต๋๋ค.
=>
=>๋ class Constraint๋ฅผ ํํํ๊ธฐ ์ํ ์ฐ์ฐ์์ ๋๋ค. ์ด ์ฐ์ฐ์ ์์ ์ค๋ ๊ฒ๋ค์ class Constraint๋ผ๊ณ , ํ๋ฉฐ, ์ด๋ค ํจ์์ ๋ค์ด๊ฐ๋ ํ์ ์ ์ ํํฉ๋๋ค.
ํจ์๋ช :: class Constraint => ํจ์ ํ์ ์ ์ธ
(==) :: Eq a => a -> a -> Bool์์ ==์ ๊ฒฝ์ฐ, Eq ์ด๋ผ๋ Type Class๋ฅผ ๊ตฌํํ๊ณ ์๋ a ๋ผ๋ Type์ 2๊ฐ ๋ฐ์ Bool์ ๋ฐํํ๋ค ๋ผ๋ ์๋ฏธ๊ฐ ๋ฉ๋๋ค.
Type Class๋ฅผ ๊ตฌํํ๊ณ ์๋ Type์ Type Class์ Instance๋ผ๊ณ ํฉ๋๋ค.
==๊ฐ ์ด๋์์ ์ฌ์ฉ์ด ๊ฐ๋ฅํ์ง๋ฅผ ์๊ฐํด ๋ณด์์ ๋, ์์ ์ ํฌ๊ฐ ์ฐ๋ 3, 'c', "hi" ๋ฑ๋ฑ์ Type๋ค๋ Eq ํ์ ํด๋์ค์ Instance ์์ ์ ์ ์์ต๋๋ค.
6. Haskell์์์ ์ผ๋ฐ์ ์ธ Type Class
1. Eq
Eq๋ ํ์ ์ด ์๋ก ๊ฐ์์ง๋ฅผ ๊ฒ์ฌํฉ๋๋ค. (Equatable)
์ด Type Class์ ์ํ๋ Type์ด ๊ตฌํ ํด์ผํ๋ ํจ์๋ ==, /= ์ ๋๋ค.
๋ง์ผ ์ด๋ค ํจ์์ ํ์ ์ Eq ์ Class Constraint๊ฐ ๋ค์ด๊ฐ๋ค๋ฉด, ๊ทธ ํจ์๋ ๋ด๋ถ์ ์ผ๋ก ==, /=๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ ์๋ฏธ ์ ๋๋ค.
5 == 5 //True
'c' /= 'b' //True
'c' /= 'c' //FalseHaskell์ ๋ ํผ๋ฐ์ค์์๋ 5 ์ ํ์ ์ธ Num์ด Eq๋ฅผ Class Constraint๋ก ๊ฐ๊ณ ์์์ ์ ์ ์์ต๋๋ค.
http://zvon.org/other/haskell/Outputprelude/Num_c.html
ํจ์์ ์ ์ถ๋ ฅ ํ์ ๋ค์ ์ ์ธํ, ๋ชจ๋ ํ์ค์ผ ํ์ค ํ์ ์ด Eq๋ฅผ ๊ตฌํํฉ๋๋ค.
2. Ord
- Ord๋ ์๋ก ๋น๊ตํ ์ ์๋ ์ฐ์ฐ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค. (Order)
- <, <=, >, >=, compare ๊ฐ ์์ต๋๋ค
- <=, >= ์์ ๋ณด๋ค์ํผ, Eq์ Class Constraints๋ก ๊ฐ์ง๋๋ค. Eq์ด ํญ๋ฑ ๋น๊ต๋ฅผ ํ๊ธฐ ๋๋ฌธ์ด์ฃ .
:t (<)
(<) :: Ord a => a -> a -> Boolhttp://zvon.org/other/haskell/Outputprelude/index.html
์์์ ํ์ ํ์ธ์ด ๊ฐ๋ฅํฉ๋๋ค.
compare
compare๋ ์ธ์๋ก ๊ฐ์ ํ์ 2๊ฐ๋ฅผ ๋ฐ์ Ordering (GT ๋ด๋ฆผ์ฐจ์, LT ์ค๋ฆ์ฐจ์, EQ ๊ฐ์)๋ฅผ ๋ฐํํฉ๋๋ค.
compare 1 3
=> LT //์ค๋ฆ์ฐจ์
compare 3 1
=> GT //๋ด๋ฆผ์ฐจ์
compare 2 2
=> EQ //๊ฐ์
compare 2 (-2) //์์๋ ๊ผญ ()๋ก ๊ฐ์ธ์ฃผ์ ์ผ ํฉ๋๋ค
=> GT
//์ค์ ์ฐ์ฐ์์ฒ๋ผ ์ฌ์ฉํ๊ธฐ ์ํด ``(backqoute)๋ก ๊ฐ์ธ ์ฌ์ฉ ํ ์ ์์ต๋๋ค.
3 `compare` 4
=> LT๋น๊ต ์ฐ์ฐ์๋ฅผ ์ฌ์ฉ๊ฐ๋ฅํ ๋ชจ๋ Type์ด ์ด Ord๋ฅผ Class Constraints๋ก ๊ฐ์ต๋๋ค.
3. show
show ํ์ ํด๋์ค๋ฅผ ๊ตฌํํ๋ Instance๋ค์ ๋ฌธ์์ด์ฒ๋ผ ํ์ ๋ ์ ์์ต๋๋ค.
show ํจ์๋ฅผ ์ฌ์ฉํด, ์ด ํ์ ์ ๊ตฌํํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
show 3
=> "3"
show 'c'
=> "'c'"
show "hello"
=> "\"hello\""http://zvon.org/other/haskell/Outputprelude/index.html
ํจ์ ํ์ ์ ์ธ, ์ผ๋ฐ์ ์ธ ํ์ ๋ค์ ๋ชจ๋ show๋ฅผ ๊ตฌํํ๊ณ ์์ต๋๋ค.
4. read
- readํ์ ์ show์ ๋ฐ๋์ ๋๋ค.
- ๋ฌธ์์ด์ ์ฝ์ด๋ค์ฌ, Read ์ธ์คํด์ค๋ก ๋ณํํด์ค๋๋ค.
- 4, 'c' ๋ฑ๋ฑ์ ๊ธฐ๋ณธ์ ์ธ ํด๋์ค๋ค์ ๋ชจ๋ ์ด ํ์ ํด๋์ค๋ฅผ class constraint๋ก ๊ฐ๊ณ ์์ต๋๋ค
read "5" // ๋ฌธ์์ด๋ก ๋ฐ๊ธฐ ๋๋ฌธ์, ์ปดํ์ผ๋ฌ๋ ์ด ๊ฐ์ด ์ ๋ง ๋ฌธ์์ด์ธ์ง, ์๋๋ฉด Int์ 5์ธ์ง๋ฅผ ์ ์ ์์ด ์๋์ฒ๋ผ ์๋ฌ๋ฅผ ๋ฑ์ต๋๋ค. ์ ๋ณด๊ฐ ๋ถ์กฑํด์ ํ์ ์ถ๋ก ์ ์คํจํ ๊ฒ์ด์ฃ .
*** Exception: Prelude.read: no parse read
read "5" - 2 // ์ด์ฒ๋ผ ๋ค๋ฅธ ํํ์์ด ๋ค์ด๊ฐ๋ฉด ํ์ค์ผ ์ปดํ์ผ๋ฌ๊ฐ ํ์ ์ ์ถ์ ํ ์ ์์ด ์ค๋ฅ๋ฅผ ๋ด์ง ์์ต๋๋ค. ์บ์คํ ์ด ํ์ ์๋ ๊ฒ์ด ์ฅ์ ์ด๋ค์.
=> 3
[read "True", False, read"False"]
=> True, False, False
read "5" :: Int // :: (Type Annotation) ์ด๋ ๊ฒ ๋จ๋ ์ผ๋ก ์ฐ์ผ ๋๋ :: ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด ํ์ ์ ๋ช ์ํด์ฃผ๋ฉด ์๋ฌ๊ฐ ๋์ง ์์ต๋๋ค.
=> 5
read "5" :: Float
=> 5.0
5. Enum
- ์ฐ์์ ์ผ๋ก ์์๊ฐ ์๋ ํ์ ์ ๋๋ค. ๋ฆฌ์คํธ์ ๋ค์ด๊ฐ ์ ์๋ ํญ๋ชฉ๋ค์ด ์ด Type Class๋ฅผ class constraint๋ก ๊ฐ์ต๋๋ค.
- succ(๋ค์), pred (์ด์ ) ํจ์๊ฐ ์ฌ๊ธฐ์ ์ํฉ๋๋ค.
- Int, Char, () (๋น ํํ) ์ด ์ด ํด๋์ค์ ์ํฉ๋๋ค.
- ์ด Type Class๋ Range๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ํด ์ค๋๋ค.
succ 'a'
=> 'b'
pred 'a'
=> '`'
pred 'b'
=> 'a'
['a' .. 'c']
=> a, b, c
[1 .. 3]
=> 1, 2, 36. Bounded
- ์ด Type Class๋ฅผ ๊ตฌํํ ์ธ์คํด์ค๋ค์ minBound์ maxBound๋ฅผ ๊ฐ์ต๋๋ค.
- ์๋ก Int, Bool, Tuple(์์๊ฐ ๋ชจ๋ Bounded๋ฅผ ๊ตฌํํ ๊ฒฝ์ฐ) ๋ฑ์ด ์์ต๋๋ค.
- minBound, maxBound์ ํ์ ์ Bounded a => a์ ๋๋ค. ๋๋ ค์ฃผ๋ ๊ฐ์ ์์์ธ๋ฐ์, ์ด ์์ a๋ ์ด Bounded๋ฅผ ๋ง์กฑํ๋ ์ด๋ค ํ์ ๋ ๋ ์ ์์ต๋๋ค. ์ด๋ฅผ ๋คํ ์์๋ผ๊ณ ํฉ๋๋ค. (Polymorphic constant)
:t minBound
=> minBound :: Bounded a => a
minBound :: Int
=> -9223372036854775808
minBound :: Float //Float ํ์ ์ ์ด๋ฅผ ๊ตฌํํ์ง ์์ต๋๋ค.
<interactive>:5:1: error:
• No instance for (Bounded Float) arising from a use of ‘minBound’ •
In the expression: minBound :: Float In an equation for ‘it’: it = minBound :: Float
minBound :: Bool
=> False7. Num
- ์ซ์๋ฅผ ๋ํ๋ด๋ ํด๋์ค
- Show, Eq์ class constraints๋ก ๊ฐ์ต๋๋ค.
- ์ด๋ฅผ ๊ตฌํํ๋ Instance ๋ก๋ Integral, Floating์ด ์์ต๋๋ค.
:t 3
3 :: Num p => p
//Num ํ์ ํด๋์ค์ ์ธ์คํด์ค ๋ฌด์์ด๋ ๋ ์ ์์ต๋๋ค. ์ด ๋ํ ์์ ๊ฐ์ ๋คํ ์์ ์ด๊ธฐ ๋๋ฌธ์ด์ฃ . Float๋, Int๋, Integer๋ก๋ ๋ ์ ์์ต๋๋ค.
// :: ์ผ๋ก ํ์ ์ ๋ฐ๊ฟ์ ์ฌ์ฉํ ์ ๋ ์์ต๋๋ค.
3 :: Integer
=> 3
3 :: Float
=> 3.0
3 :: Double
=> 3.0๋ค๋ง, * , /,-,+ ๋ฑ์ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ๋๋ ๊ฐ์ ํ์ ์ด์ด์ผ ํ๊ธฐ ๋๋ฌธ์, ์ผ๋ถ๋ฌ ์๋์ ๊ฐ์ด Num์ ๋ค๋ฅธ ์ธ์คํด์ค๋ก ํ๋ณํ ํด์ ๊ณ์ฐํ๋ ค๊ณ ํ๋ฉด ๋์ํ์ง ์์ต๋๋ค.
:t (*)
(*) :: Num a => a -> a -> a
(5 :: Int ) * (10::Integer) //๊ฐ์ Num ์ผ์ง๋ผ๋ ํ์ ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๊ณ์ฐ์ด ์๋ฉ๋๋ค.
=> <interactive>:27:16: error:Integral
์ ์๋ฅผ ๋ํ๋ด๋ Type Class ์ ๋๋ค. Int, Integer๊ฐ ์ฌ๊ธฐ์ ์ํฉ๋๋ค.
fromIntegral์ด๋ผ๋ ํจ์๋ก, Integral์ Num์ผ๋ก ๋ณํ์ํฌ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ ํด์, Float ๋ฑ ๋ค๋ฅธ Num ๋ด์ ํ์ ๋ค๊ณผ ๊ณ์ฐ์ ๊ฐ๋ฅํ๊ฒ ํ ์ ์์ต๋๋ค.
fromIntegral (4::Int) //์ฌ๊ธฐ์ 4๋ Int ์ด๊ณ , ๋คํ ์์๊ฐ ์๋๋๋ค.
=> 4 //์ฌ๊ธฐ์๋ Num a
//์ด ๊ฒฝ์ฐ, Float๊ณผ ๋ง์ ์ด ๋์ง ์์ต๋๋ค.
:t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b //Integral a๋ฅผ ๋ฐ์, Num์ธ b๋ฅผ ๋๋ ค์ค๋๋ค.
//fromIntegral์ ์ฌ์ฉํ๋ฉดFloating
๋ถ๋์์์ ์ ๋ํ๋ด๋ Type Class์ ๋๋ค. Float, Double์ด ์ฌ๊ธฐ์ ์ํฉ๋๋ค.
7. ๋ง๋ฌด๋ฆฌ
Type Class๋ ๊ฐ์ฒด ์งํฅ์์์ ํด๋์ค์๋ ๋ฌ๋ฆฌ, ๊ทธ์ ๊ตฌํํด์ผ ํ๋ ํจ์์ ํ์ ๋ง์ ์ง์ ํฉ๋๋ค.
1. ํ๋์ ํ์ ์ด ์ฌ๋ฌ Type Class์ Instance(๊ตฌํ์ฒด)๊ฐ ๋ ์ ์๊ณ
2. ํ๋์ Type Class๋ ์ฌ๋ฌ ํ์ ์ ๋์์ Instance๋ก ๊ฐ์ง ์ ์์ต๋๋ค.
3. ๊ทธ๋ฆฌ๊ณ ํ Type Class๊ฐ ๋ค๋ฅธ Type Class๋ฅผ ๊ฐ์ ธ๊ฐ๊ธฐ ์ํ ์ ์ ์กฐ๊ฑด์ด ๋๊ธฐ๋ ํฉ๋๋ค.
๋ํ์ ์ผ๋ก Num ํด๋์ค๋ Show ์ Eq๋ฅผ ๋์์ ๊ตฌํํ๊ณ ์๊ณ , ๋ Eq ์ Num ๋ฟ๋ง์ด ์๋๋ผ Char๋ Instance๋ก ๊ฐ์ต๋๋ค.
๋ํ Ord์ ์ธ์คํด์ค๊ฐ ๋๋ ค๋ฉด ๊ทธ ์ ์ Eq์ ์ธ์คํด์ค๊ฐ ๋์ด์ผ ํ์ฃ . (์ ์ ์กฐ๊ฑด์ด๋ผ๊ณ ํฉ๋๋ค)
์ด๋ ๊ฒ 2์ฅ Type ์ ์ ๋ฆฌ๊ฐ ๋๋ฌ์ต๋๋ค...
+ ํ๋ด...
์ผ์์... ํ์คํ Type Class๋ ์ผ๋ฐ์ ์ธ ๊ฐ์ฒด ์งํฅ์ ํด๋์ค๊ฐ ์๋๋๋ค. ํจ์์ ์ ์๋ง ํด๋๊ณ , ๊ทธ๊ฑธ ๊ฐ๊ฐ์ ํ์ ์ด ๊ฐ์ ธ๊ฐ์ ์์์ ๊ตฌํํ๋๋ก ๋ด๋ฒ๋ ค๋ก๋๋ค. ๊ทธ๋ฆฌ๊ณ TypeClass๋ ์์์ ๊ฐ๋ ๋ณด๋ค๋ ์ ์ ์กฐ๊ฑด๊ฐ์ ์ํ์ด๋ ๋ ผ๋ฆฌํ์์์ '์กฐ๊ฑด'์ ๊ฐ๊น์ด ๊ฒ ๊ฐ์ต๋๋ค.
์, ์ฑ ์์ ๋ณด๋ ๊ฑด ์กฐ๊ธ ํท๊ฐ๋ ธ๋๋ฐ, ์ค์ API ๋ฌธ์์์ ํด๋น ํด๋์ค์ ์ฐพ์์ ๋ณด๋ ์กฐ๊ธ ๋ ์ดํด๊ฐ ์ ๋์์ต๋๋ค.
(์์ง ๋ฐฐ์ด ์ ์ด ์๋ ๋ฌธ๋ฒ๋ค์ด ๋์ค์ง๋ง...)
์ด ๋ค์์ ๋๋์ด ์ฌ๊ท ๋ฑ๋ฑ์ ์ด์ฉํด ๋ฐ๋ณต๋ฌธ์ ๊ตฌํํ ์ ์๋ 3์ฅ์ ๋๋ค!! ์ด์ 3์ฅ์ ๋ณด๋ฉด HackerRank์ 2๋ฒ ๋ฌธ์ ๋ฅผ ํ ์ ์์ด์... ๊ฐ๊ฒฉ... ์ด์ 3์ฅ์ ๋นจ๋ฆฌ ์ ๋ฆฌ ํ ์ ์์ผ๋ฉด ์ข๊ฒ ๋ค์. ๋ค์ ์ฃผ์๋ 3์ฅ์ผ๋ก ๋ต๊ฒ ์ต๋๋ค. ๊ทธ ๋ HackRank ๋ฌธ์ ๋ ํ ๋ฒ ํ์ด๋ณผ๊ฒ์!
์ฆ๊ฑฐ์ด ๋ฐค ๋์๊ธธ ๋ฐ๋๋๋ค :)
'๊ณต๋ถ > ํจ์ํ ํ๋ก๊ทธ๋๋ฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Haskell] 5 - 3. $ (function application) (0) 2020.02.16 [Haskell] #6 syntax-in-functions (1) (0) 2020.01.27 [Haskell] #4 Type (1) (0) 2020.01.19 [Haskell] #3 Tuple (0) 2020.01.12 [Haskell] #2 List(2) Range, list comprehension (0) 2020.01.12