그 수업에서, 제 일생에 있어 첫번째 프로그램을 Haskell로 만들 수 있었습니다.
세미나는...
과제 자체는 간단했습니다. 5*5 비트맵으로 주어지는 1~4의 숫자를 인식하는 프로그램을 만들 것. C언어를 갓 배운 학생에게는 적당한 수준의 과제라고 생각합니다.[footnote]제대로 문자 인식은 방대한 문자DB가 필요하니까요.[/footnote] 알고리즘도 직접 생각해서 만들라는 것이었습니다만, 생각해볼 시간은 별로 없었습니다. 1주일만에 만들어야 하니까요.
그때, 전 Haskell이라는 언어를 사용해도 괜찮겠냐고 선생님에게 물었습니다. C 언어는 다른 수업에서도 계속 듣고 있어서 포인터만 안쓰면 대충 쓸 수 있었습니다만, Haskell로 푸는 것이 더 편할 것 같았기 때문이었습니다. C 언어로 배열을 다루는 건 좀 복잡할 거 같고, 그럴 바에야 tail같은 함수가 있는 Haskell이 편하지 않을까 하는 단순한 생각에서요.
그 당시 제가 사용할 수 있었던 함수라고는 map, head, tail, length 등 아주 기초적인 것 뿐이었습니다. ( 코드를 보시면 그때의 제 수준을 아실 수 있을 겁니다. ) Haskell에 관한 책을 가지고는 있었지만, 영어로 쓰여있어서 읽을 엄두도 못내고 있었고요.
그래도, C보다는 낫겠지라는 마음에, 선생님께 무작정 하겠다고 말씀드리고나서 산 책이 'ふつうのHaskellプログラミング'라는 책이었습니다. 모나드 같은건 잘 모르겠고, 당장 I/O를 사용해야겠고. 이럴때 도움이 되는건 일본책이다[footnote]일본책은 대체적으로 이론적인 면은 부족해도, 실용적이라고 자주 들었으니까요. 저자도 Haskell보다는 Ruby가 메인이라네요.[/footnote]라는 생각에 샀습니다.
이 책과 함께 일주일안에 프로그램을 짜는데 집중했습니다. 덜 중요한 다른 수업시간에는 어떻게 알고리즘을 코드로 바꿀 수 있을까 생각하면서, 모듈을 어떻게 불러야 하나 같은 기초적인 문제로도 고민하고 있었습니다.
more..
그 일주일간의 코딩 결과가 바로 이 프로그램입니다.
[code]
import Char
import List
--------Data Declair ---------
data FundamentalData =
FD { cMarkList :: [[[Int]]],
cSampleList :: [[[Int]]],
cMarkNameList :: [String],
cSampleNameList :: [String],
cNX :: Int,
cMY :: Int,
cILM :: Int,
cJKS :: Int
}
data MarkPatternProfile =
MPP { mName :: String,
mSerial :: Int,
mProb :: Double,
mPattern :: [[Int]],
mSliceX :: [Int],
mSliceY :: [Int],
mUnitImpcy :: Double
}
data SamplePatternProfile =
SPP { sSerial :: Int,
sPattern :: [[Int]],
sSliceX :: [Int],
sSliceY :: [Int]
}
---------------- Make Profile ----------------
makeMPP _name _serial _Probability _n _m _Pattern =
MPP (_name)
(_serial)
(_Probability)
(_Pattern)
(oneProfile _n _m _Pattern)
(oneProfile _n _m $ transList _n _m _Pattern)
0
makeSPP _serial _n _m _Pattern =
SPP (_serial)
(_Pattern)
(oneProfile _n _m _Pattern)
(oneProfile _n _m $ transList _n _m _Pattern)
----------------------------------------------------------------
listZip _List = map head $group _List
patternSum :: [[Int]] -> Int
patternSum _Pattern = sum $ concat $ _Pattern
map1 f a [] = []
map1 f a (b:bs) = f a b : map1 f a bs
map2 f a b [] = []
map2 f a b (c:cs)=f a b c : map2 f a b cs
map3 f a b c [] = []
map3 f a b c (d:ds)=f a b c d : map3 f a b c ds
map3t f l k m [] = []
map3t f l k m (n:ns) = f l k n m : map3t f l k m ns
map3tt f l k nList m = map3t f l k m nList
map4 f a b c d [] = []
map4 f a b c d (e:es)=f a b c d e : map4 f a b c d es
map5 f a b c d e[] = []
map5 f a b c d e (g:gs)=f a b c d e g : map5 f a b c d e gs
map5t f a b l k m [] = []
map5t f a b l k m (n:ns) = f a b l k n m : map5t f a b l k m ns
map5tt f a b l k nList m = map5t f a b l k m nList
map2a f (x:xs) (y:ys) = f x y : map2a f xs ys
mapxy f _List1 _List2 = map2 map1xyx f _List2 _List1
mapxyx f _List2 _1 = map1 f _1 _List2
map1xy f a _List1 _List2 = map3 map1xyx f a _List2 _List1
map1xyx f a _List2 _1 = map2 f a _1 _List2
mapNxy f _L1 _L2 = map2 mapNxyx f _L2 [0..(_L1)]
mapNxyx f _L2 _1 = map1 f _1 [0..(_L2)]
map1Nxy f a _L1 _L2 = map3 map1Nxyx f a _L2 [0..(_L1)]
map1Nxyx f a _L2 _1 = map2 f a _1 [0..(_L2)]
map2Nxy f a b _L1 _L2 = map4 map2Nxyx f a b _L2 [0..(_L1)]
map2Nxyx f a b _L2 _1 = map3 f a b _1 [0..(_L2)]
mapForMPPL f [] _ _ _ _ _ = []
mapForMPPL f (a:as) (b:bs) c d e (g:gs) = f a b c d e g : mapForMPPL f as bs c d e gs
mapForSPPL f _ _ _ [] = []
mapForSPPL f (a:as) b c (d:ds) = f a b c d : mapForSPPL f as b c ds
functionMap _ _ [] = []
functionMap func1 func2 (x:xs) = func1 (func2 x) : functionMap func1 func2 xs
--------Fundamental Function--------
pickPixelP _Pattern n m = _Pattern!!m!!n
--Read Function
--Split Function
splitByLength a _Source = if (length _Source) < a
then []
else [take a _Source]++(splitByLength a (drop a _Source))
filtern _LList _zqzL = length $ filter (_zqzL==) _LList
headfilter _ [] = []
headfilter a (x:xs)
| a (head x) = x:headfilter a xs
| otherwise = headfilter a xs
makeMarkList _OriginList = map tail _OriginList
--결과값은 [[Str]]
makeMarkNameList _OriginList = map head _OriginList
--결과값은 [[Str]]
--Trans Function
transOneInt2Char _List n m = chr (_List!!m!!n +48)
transOneChar2Int _List n m = ord (_List!!m!!n) -48
transListStr2Int _lengthN _lengthM _List= map1Nxy transOneChar2Int _List (_lengthN-1) (_lengthM-1)
transListInt2Str _lengthN _lengthM _List= map1Nxy transOneInt2Char _List (_lengthN-1) (_lengthM-1)
--------Analazying--------
transList _n _m _Pattern = map1Nxy pickPixelP _Pattern (_n-1) (_m-1)
--transList _n _m _Pattern = map1Nxy pickPixelP _Pattern _n _m
analyzePattern _n _m _Pattern = map2 analyzeXPattern _Pattern _n [0..(_m-1)]
analyzeXPattern _Pattern _n m= div (sum $ map1 analyzeTwoPixel (_Pattern!!m) [0..(_n)]) 2
analyzeTwoPixel _PatternX n = if (pickPixelXPattern ([0]++_PatternX++[0]) n) == (pickPixelXPattern ([0]++_PatternX++[0]) (n+1))
then 0
else 1
pickPixelXPattern _XPattern n = _XPattern!!n
preFilter4LZ _List = filter (0 /=) _List
oneProfile _n _m _Pattern = listZip $ preFilter4LZ $ analyzePattern _n _m _Pattern
--profiles _n _m _PatternList = map2 oneProfile _n _m _PatternList
---------------- Compare With MPP & SPP ----------------
comparePPL _ [] = []
comparePPL _MPPList _SPPList = compareMPPL (head _SPPList) _MPPList : comparePPL _MPPList (tail _SPPList)
--jointMPPL :: [MarkPatternProfile] -> [Bool] -> [MarkPatternProfile]
jointMPPL _ [] = []
jointMPPL _MPPList _cMPPL
| (head _cMPPL) = (mName (head _MPPList)) : jointMPPL (tail _MPPList) (tail _cMPPL)
| otherwise = jointMPPL (tail _MPPList) (tail _cMPPL)
jointMPPL1 _ [] = []
jointMPPL1 _MPPList _cMPPL
| (head _cMPPL) = (head _MPPList) : jointMPPL1 (tail _MPPList) (tail _cMPPL)
| otherwise = jointMPPL1 (tail _MPPList) (tail _cMPPL)
--compareMPPL :: (Show MarkPatternProfile) => SamplePatternProfile -> [MarkPatternProfile] -> [Bool]
compareMPPL _SPP _MPPList = map (compareMPP _SPP) _MPPList
compareMPP :: SamplePatternProfile -> MarkPatternProfile -> Bool
compareMPP _SPP _MPP
| and [(sSliceX _SPP == mSliceX _MPP),
(sSliceY _SPP == mSliceY _MPP)
] = True
| otherwise = False
s11 = SPP 00 [[1,1],[2,2]] [1,2,1] [1,2,3,2,1]
m12 = MPP "00" 00 0.2 [[1]] [1,2,2,1] [1,2,3,2,1] 0
m11 = MPP "00" 00 0.2 [[1]] [1,2,1] [1,2,3,2,1] 0
dating a b
| and [(a==b), (a*a==b*b)] = True
|otherwise = False
---------------- OutPut Function ----------------
plusLineEndOfString _List = (concat $ map show _List)++"\n"
print2DList _List
| length _List == 1 = putStr $ plusLineEndOfString (head _List)
| otherwise = do putStr $ plusLineEndOfString (head _List)
print2DList (tail _List)
print2DListMap _List
| length _List == 1 = print2DList (head _List)
| otherwise = do print2DList (head _List)
putStr "\n"
print2DListMap (tail _List)
main = do
---------------- Source 입력 ----------------
cs <- getContents
---------------- 기본 파일 입력 ----------------
let source = lines cs
---------------- 파일 분석 ----------------
let lList = map length source
let zszLList = listZip $ sort $ listZip lList
let noLList = map1 filtern lList zszLList
let sNoLList = sort noLList
---------------- Foundation Data 제작 ----------------
let g = div (last sNoLList) (sum $ init sNoLList) -- g for m for y
let h = length $ source!!1 -- h for n for x
---------------- Source -> Origin -> mark/sample,name/mark 분리 ----------------
let originList = splitByLength (g+1) source --OriginList는 mark+name인 원소.
let markNameList = map1 drop 4 $ headfilter ('m'==) $ makeMarkNameList originList
let sampleNameList = map1 drop 6 $ headfilter ('s'==) $ makeMarkNameList originList
let markList = map2 transListStr2Int h g $ take (length markNameList) $ makeMarkList originList
let sampleList = map2 transListStr2Int h g $ drop (length markNameList) $ makeMarkList originList
----------------Foundation Data 제작----------------
let i = length markList -- l for Mark
let j = length sampleList -- k for Sample
let mX = (quot h 2)-1
let mY = (quot g 2)-1
---------------- Call mark/sample Function ----------------
let mark l = markList!!l
let sample k = sampleList!!k
---------------- Foundation Function - Picking ----------------
let pickPixelS k n m = (sample k)!!m!!n
let pickPixelM l n m = (mark l)!!m!!n
---------------- Start Test Code ----------------
let mPPL = mapForMPPL makeMPP markNameList [0..] 0 h g markList
let sPPL = mapForSPPL makeSPP [0..] h g sampleList
print $ map2 analyzePattern h g $ markList ++ sampleList
print2DList $ sPattern (sPPL!!6)
print $ comparePPL mPPL sPPL
putStr "\n1st Sample is"
print $ jointMPPL mPPL (compareMPPL (sPPL!!0) mPPL)
putStr "\n2nd Sample is"
print $ jointMPPL mPPL (compareMPPL (sPPL!!1) mPPL)
putStr "\n3rd Sample is"
print $ jointMPPL mPPL (compareMPPL (sPPL!!2) mPPL)
putStr "\n4th Sample is"
print $ jointMPPL mPPL (compareMPPL (sPPL!!3) mPPL)
putStr "\n5th Sample is"
print $ jointMPPL mPPL (compareMPPL (sPPL!!4) mPPL)
putStr "\n6th Sample is"
print $ jointMPPL mPPL (compareMPPL (sPPL!!5) mPPL)
print $ jointMPPL [m11,m12] (compareMPPL s11 [m11,m12])
[/code]
시간이 되면 추가하려고 했던 미구현 코드가 있어서 그것만 지우고 올리려고 했는데, 사용하던 함수를 지웠는지 Type Error가 나서 잠시 고생했습니다.
이 코드가 당시의 제 최대 실력이었습니다.
Prelude에서 제공해주는 함수 조차 몰라서 만들어서 쓸 정도로 엉터리 코드입니다.
새로운 종류의 map 함수가 도대체 몇개인지... 아직 어떻게 함수를 결합시켜야 하는지 몰랐다는 기록들입니다.
그렇지만, 이것은 저에게 있어서는 새로운 도전이었고, 이때만큼 제대로 프로그래밍 공부했던적이 없었죠.
그러나 1주일밖에 없는 짧은 공부·개발 시간만 아니었다면, 다른 레포트때문에, 시험기간이라서 급하게 공부하지만 않았다면 같은 아쉬움은, 항상 마음에 남아있었습니다.
그 이후로는 학교에서 C언어 과제를 본격적으로 내주기 시작해, Haskell에 눈을 돌릴 새가 없었습니다. 포인터가 어쩌고 저쩌고 항상 디버깅하는데 시간이 잘만 가더군요.
방학에도 항상 놀다가, 이번에 드디어 영어책을 '조금' 읽기 시작했습니다.
이전에도 몇번이나 읽으려고 시도했지만, 항상 '뭔말인지 모르겠다' 같은 부분에서 막히면 그만두곤 했습니다.
알고 있다고 생각한 부분을 건너뛴 것이 문제였는데, 처음부터 차근차근 읽어보니까 중요한 이야기가 너무 많았습니다. 원래부터 그랬었다면 재미있게 했을텐데, 아쉬움이 남습니다.
그래도 이제 다시 시작하게 되었으니 열심히 하려고 합니다.
그 첫 단계로, 이 프로그램을 한번 고치고 싶습니다.
옛날 프로그램을 손보는 거보다는 새로운 것을 만드는게 낫다고도 합니다만, 버그가 남아 있는 것은 아직까지도 마음에 걸려서, 최소한 버그라도 고쳐놓고, 그 다음 걸음을 내딛으려 합니다.







