Ruby 프로그래밍 5 한글자소처리 한국어정보의전산처리 2017. 5. 15.
잉글랜드프리미어리그전적통계내기 http://www.football-data.co.uk/mmz4281/1617/e0.csv res = Hash.new() # 결과저장할 data structure: 팀 => 전적 (8 개필드 ) teams = [ Arsenal, Bournemouth, Burnley, Chelsea, Crystal Palace, Everton, Hull, Leicester, Liverpool, Man City, Man United", "Middlesbrough", "Southampton", "Stoke", "Sunderland", "Swansea", "Tottenham", "Watford", "West Brom", "West Ham"] # 20 개팀이름으로이루어진 Array 를만듦 teams.each do team #res Hash 에 20 개팀을추가하되 res[team] = Array.new(8,0) # 아래의 8 개필드를 0 으로초기화 # 0 1 2 3 4 5 6 7 # match, won, draw, lost, goal_won, goal_lost, goal_diff, point
File.open("E0.csv") do f f.each do line if f.lineno!= 1 fs = line.split(",") #fs[2], fs[3], fs[4], fs[5], fs[6] #HT, AT, FTHG, FTAG, FTR fs[4] = fs[4].to_i fs[5] = fs[5].to_i res[ fs[2] ][4] += fs[4] # 홈팀득점 fs[4] 만큼증가 res[ fs[2] ][5] += fs[5] # 홈팀실점 fs[5] 만큼증가 res[ fs[3] ][4] += fs[5] # 어웨이팀득점 fs[5] 만큼증가 res[ fs[3] ][5] += fs[4] # 어웨이팀실점 fs[4] 만큼증가 case fs[6] when "H" # 홈팀승리 res[ fs[2] ][1] += 1 # 홈팀승리 1 증가 res[ fs[3] ][3] += 1 # 어웨이팀패배 1 증가 when "A # 어웨이팀승리 res[ fs[2] ][3] += 1 # 홈팀패배 1 증가 res[ fs[3] ][1] += 1 # 어웨이팀승리 1 증가 when "D # 무승부 res[ fs[2] ][2] += 1 # 홈팀무승부 1 증가 res[ fs[3] ][2] += 1 #Away 팀무승부 1 증가 else ; # 위의세경우외에는없음
res.each do team, data #res Hash의경기수, 득실점차, 승점채우기 data[0] = data[1]+data[2]+data[3] # 경기수 = 승리수 + 무승부수 + 패배수 data[6] = data[4] - data[5] # 득실점차 = 총득점 - 총실점 data[7] = data[1] * 3 + data[2] # 승점 = 승리수 *3 + 무승부수 $, = ", # 필드구분자를쉼표로. print 함수호출시쉼표자리에출력됨 $\ = \n # 레코드구분자를줄바꿈문자로 i = 0 # 순위변수 res.sort{ x,y y[1][7]<=>x[1][7] }.each do team,data # 승점순으로소팅 i += 1 # 순위를 1씩증가시킴 print i, team, data.join(",") # 순위, 팀이름, 팀전적출력
한글자소 ( 초성, 중성, 종성 ) 분해 : called code @onset_name_array = [" ㄱ "," ㄲ "," ㄴ "," ㄷ "," ㄸ "," ㄹ "," ㅁ "," ㅂ "," ㅃ "," ㅅ "," ㅆ "," ㅇ "," ㅈ "," ㅉ "," ㅊ "," ㅋ "," ㅌ "," ㅍ "," ㅎ "] # 19 개. 다른파일에서사용할변수는전역 (global) 변수로해야함 @nucleus_name_array = [" ㅏ "," ㅐ "," ㅑ "," ㅒ "," ㅓ "," ㅔ "," ㅕ "," ㅖ "," ㅗ "," ㅘ "," ㅙ "," ㅚ "," ㅛ "," ㅜ "," ㅝ "," ㅞ "," ㅟ "," ㅠ "," ㅡ "," ㅢ "," ㅣ "] # 21 개. 전역변수는앞에 @ 를붙여줌 @coda_name_array = ["@"," ㄱ "," ㄲ "," ㄳ "," ㄴ "," ㄵ "," ㄶ "," ㄷ "," ㄹ "," ㄺ "," ㄻ "," ㄼ "," ㄽ "," ㄾ "," ㄿ "," ㅀ "," ㅁ "," ㅂ "," ㅄ "," ㅅ "," ㅆ "," ㅇ "," ㅈ "," ㅊ "," ㅋ "," ㅌ "," ㅍ "," ㅎ "] # 28 개 def is_hangeul?(code) # 어떤문자의코드값을입력으로하여한글인지판정 (0xac00..0xd7a3).include? code def get_onset(code) # 초성값알아냄. 반환값은 0~18, 0: ㄱ. 1: ㄲ,, 18: ㅎ (code 0xac00) / 588 # x=( 입력문자코드값 - 가 의코드값 ); x 를 588 로나눈몫 def get_nucleus(code) # 중성값알아냄. 반환값은 0~20, 0: ㅏ, 1: ㅐ,..., 20: ㅣ ( (code - 0xac00) % 588 ) / 28 #x 를 588 로나눈나머지 ( 초성블록순위 ) 를 28 로나눈몫 def get_coda(code) # 종성값알아냄. 반환값은 0~27, 0: 받침없음, 1: ㄱ, 2: ㄲ,... 27: ㅎ (code - 0xac00) % 28 # x 를 28 로나눈나머지
한글자소 ( 초성, 중성, 종성 ) 분해 : calling code require './jaso_call_me.rb' raise " 초, 중, 종성을알려드립니다. 한글을입력하세요." if ARGV.size < 1 code = ARGV[0].ord # 사용자가입력한문자의코드값을알아냄 $\ = "\n #print 함수호출시맨끝에줄바꿈문자출력 if is_hangeul? code # 사용자가입력한문자가한글이면 print " 초성 : ", @onset_name_array[ get_onset(code) ] print " 중성 : ", @nucleus_name_array[ get_nucleus(code) ] print " 종성 : ", @coda_name_array[ get_coda(code) ] # 초, 중, 종성값을알아내어초, 중, 종성이름 Array에서추출 else print " 한글을입력하세요. 입력하신문자는한글이아닙니다."
한글자소 ( 초성, 중성, 종성 ) 분해 : 문자열 ARGV[0].each_char do char # 사용자입력문자열의각문자에대해 code = char.ord print char if is_hangeul? code print ": 초성 : ", @onset_name_array[ get_onset(code) ] print ", 중성 : ", @nucleus_name_array[ get_nucleus(code) ] print ", 종성 : ", @coda_name_array[ get_coda(code) ], "\n" else print ": 한글아님 \n"
텍스트자소통계 onset_freq_array = Array.new(19,0) # [ ㄱ빈도, ㄲ빈도,..., ㅎ빈도 ] nucleus_freq_array = Array.new(21,0) # [ ㅏ빈도, ㅐ빈도, ㅑ빈도,..., ㅣ빈도 ] coda_freq_array = Array.new(28,0) # [ 없음빈도, ㄱ빈도, ㄲ빈도, ㄳ빈도,..., ㅎ빈도 ] hangeul_cnt = 0 # 한글음절개수 counter File.open(ARGV[0], "r:utf-8").each do line line.chomp.each_char do char # 각문자에대해 code = char.ord if is_hangeul? code # 한글이면 hangeul_cnt += 1 # 한글개수 counter 를 1 증가시킴 onset_freq_array[ get_onset(code) ] += 1 nucleus_freq_array[ get_nucleus(code) ] += 1 coda_freq_array[ get_coda(code) ] += 1 # 초, 중, 종성값알아내어해당빈도를 1 증가시킴
$, = "\t ; $\ = "\n # 필드구분자, 레코드구분자설정 print " 초성통계 ", "="*20 #= 을 20개출력 onset_freq_array.each_with_index do freq, idx # 초성통계자료 print @onset_name_array[ idx ], freq, freq*100/hangeul_cnt.to_f # 초성이름, 초성빈도, 백분율 ( 초성빈도 *100/ 한글음절수 ) 출력 print " 중성통계 ", "="*20 nucleus_freq_array.each_with_index do freq, idx print @nucleus_name_array[ idx ], freq, freq*100/hangeul_cnt.to_f print " 종성통계 ", "="*20 coda_freq_array.each_with_index do freq, idx print @coda_name_array[ idx ], freq, freq*100/hangeul_cnt.to_f
변수 (variable) 의종류 국소 (local) 변수 : 특정함수, 블록, 모듈, 파일안에서만유효 shadowing: 더작은 scope 에서정의된국소변수는, 더큰 scope 에서정의된자신과같은이름의국소변수를가림. 전역 (global) 변수 : 다른파일에서도접근가능. @ 로시작 instance 변수 : 어떤 class 에속하는각 instance/object 들이따로가지고있는변수. @ 로시작 해당 class 의 initialize 메소드에서 instance 변수의초기화담당 특별한조치를취하지않으면해당 class 의메소드에서만접근가능 class 변수 : 어떤 class 가가지고있는변수. @@ 로시작. 해당 class 에속하는모든 object 가공통으로접근가능함. 특별한조치를취하지않으면해당 class 의메소드에서만접근가능
instance 변수와 class 변수의예시 class Person #Person 이라는클래스정의 @@n = 0 # 이클래스에속하는 instance 의개수를기록하는변수 def initialize(name, age) # 이클래스에속하는 instance 를만들때호출됨 @name = name #instance 변수 @name 초기화 @age = age #instance 변수 @age 초기화 @@n += 1 #instance 의개수를 1 증가시킴 def to_s #instance 의정보 / 데이터를문자열로변환. print 시자동호출 name + : + age.to_s #name, 콜론, age( 문자열로변환 ) def n ; @@n ; end # 클래스변수 @@n 을외부에서조회하는메소드
free function 과 method free function: 특정 object 에매여있지않고그냥호출하는함수 호출형식 : func_name( argument_list ) class 밖에서정의됨. method: 특정 object 에매여서호출되는함수 호출형식 : object_name.method_name(argument_list ) 특정 class 내에서정의됨. 해당 class 내에정의된클래스변수에접근할수있음. 동일한기능을하는함수를 free function 으로만들수도있고메소드로만들수도있음. 예컨대한자 -> 한글변환함수를 free function 으로만들면 def sino2kor( str ) # str 가입력문자 메소드로만드는방법은후술
한자, 한글 ( 해당한자독음 ) 헤더파일 class String #String class 를열어서 data, 메소드를추가할수있음 @@hanja = { # 클래스변수는 @@ 으로시작해야함. 한자 => 한글 0x4e00=>0xc77c, #IL 일 0x4e01=>0xc815, #CENG 정 0x4e02=>0xad50, #KYO 교 0x4e03=>0xce60, #CHIL 칠 0x4e04=>0xc0c1, #SANG 상 0xfa2c=>0xad00, #KWAN 관 0xfa2d=>0xd559, #HAK 학 } # 클래스변수 @@hanja 의정의끝 #String class 를닫음
한자 -> 한글변환 raise " 한자-> 한글변환 " if ARGV.size < 1 require './unihan_hanja_read.rb' class String #String class를열어서 data, 메소드를추가할수있음 def sino2kor # 한자를한글 ( 해당한자의독음 ) 로변환하는메소드 @@hanja[ self.ord ] # 메소드는항상 object에대해호출됨 # 이메소드가호출된 object를 self로지칭 # 클래스변수는그클래스의메소드에서만접근가능 print [ ARGV[0].sino2kor ].pack 'U # 유니코드코드값을문자로변환하는 idiom: [ 코드값 ].pack U # 아스키코드값을문자로변환하는방법 : 코드값.chr
한글 -> 한자변환 : 한글을입력하면그한글을독음으로갖는한자들출력 class String @@dic = {} # 클래스변수 dic 을만들어빈 Hash 할당 @@hanja.each do k,v # 클래스변수 hanja Hash 의각 key( 한자 ) 와 value( 한글 ) 에대해 @@dic[v] = [] # 한글을 key 로하여 dic Hash 에 ( 없으면 ) 추가하고 Array 와연결시킴 @@dic[v] << k # 이한글 key 와연결된 Array 에한자추가 def kor2sino # 한글을한자로변환하는메소드 @@dic[self.ord] #dic Hash 에서한글코드값과짝을이루는한자코드값들의목록반환 res = ARGV[0].kor2sino # 사용자가입력한한글에대해 kor2sino 메소드호출 print res.size, 개 : res.each do i print [i].pack U # 각한자코드값을문자로변환하여출력