Ruby 프로그래밍 3 Array 와 Hash 이용 한국어정보의전산처리 2017. 5. 1.
Array 기초 1: 문자열소팅 사용자가 command-line에입력한문자열들을소팅하여출력 if ARGV.length < 1 print 입력한문자열들을소팅해드립니다. exit ARGV.sort.each do s puts s
Array 기초 2: 정수소팅 사용자가 command-line 에입력한정수들을소팅하여출력 if ARGV.length < 1 print 입력한정수들을소팅해드립니다. exit ARGV.map{ s s.to_i }.sort.each do i puts i # map 메소드 : Array 의각요소에대해블록안에서규정한조작에따라변형하여그결과값으로이루어진새로운 Array 를만들어 return 함.
Array 기초 3: 문자열역순소팅 역순소팅 : 문자열의뒤에서부터비교 내림차순소팅과다름. if ARGV.length < 1 print " 입력한문자열들을역순소팅해드립니다." exit ARGV.map{ s s.reverse }.sort.each do s #Array 안의요소들을뒤집어서소팅한뒤 puts s.reverse #Array 안의요소들을다시뒤집어 ( 즉본래대로하여 ) 출력
String 메소드 reverse 활용 : palindrome 검사 palindrome: 뒤집어도똑같은문자열. 예 : 바라바, 도레미레도 사용자가 command line 에입력한문자열이 palindrome 인지판정 if ARGV[0] == ARGV[0].reverse print "palindrome 입니다." else print "palindrome 아닙니다." # 방법 1 print ( ARGV[0] == ARGV[0].reverse )? Yes : No # 방법 2 ~? A : B ~ 가참이면 A 로, ~ 가거짓이면 B 로 evaluate 되는 expression
Array 메소드 include? ( 합격자 ) 명단에이름이들어있는지검사 list = [" 강감찬 "," 공형진 "," 김갑순 "," 김구라 ",," 홍길동 "] if ARGV.length < 1 print " 이름이명단에있는지검사합니다. 이름을입력하세요." print ( list.include? ARGV[0] )? Yes : No 어떤값이어떤범위에드는지알아볼때도 include? 메소드가유용 하한선 <= 값 and 값 <= 상한선 ( 하한선.. 상한선 ).include? 값 전자보다후자가더간결함.
Array 메소드 each Enumerable에대해가장많이사용되는메소드는 each임. Enumerable에들어있는각요소에대해블록에규정된일을함. 사용자가입력한정수들을각각제곱하여출력 ARGV.each do s # 사용자가 command line에입력한각문자열에대해 puts s.to_i**2 # 문자열 s를정수로변환하여, 제곱하여, 출력 # 방법 1 ARGV.map{ s s.to_i**2 }.each do i # map 메소드로정수변환과제곱을수행하고 puts i # each 메소드블록안에서는출력만수행 # 방법 2
파일입출력 : nl 흉내내기 File 클래스의 open 또는 new 메소드로파일 object 생성 이메소드의첫째인자 : 파일이름 둘째인자 : r:utf-8 ( 읽기모드 ), w:utf-8 ( 쓰기모드 ) File object에대해 each 메소드호출 : 한라인씩읽음. n=1 File.open(ARGV[0], r:utf-8 ).each do line # 사용자가제공한이름의파일을열어서, 각라인에대해 print n, ARGV[1], line #n, 구분자, line 출력 n+=1 # n을 1 증가시킴
redirection redirection 을사용하여입력파일이마치표준입력으로부터오는것처럼처리할수도있음. n=1 while line = STDIN.gets do # 표준입력으로부터읽어들여 line 에저장 # 읽어들일문자열이있으면, 즉읽기가성공하면 # 위의 assignment statement 는 true 를반환 # 읽어들일문자열이있는동안블록이반복실행됨. line.chomp! #line 맨끝의줄바꿈문자을떼어냄. mutating 메소드 print n, ARGV[0], line, "\n" #n, 구분자, line 출력 n+=1 # n 을 1 증가시킴
합격자명단파일읽어이름검사 if ARGV.length < 2 print " 이름이명단에있는지검사. 명단파일, 이름을입력 " exit names = [] File.open(ARGV[0], "r:utf-8").each do line names << line.chomp #line에서줄바꿈문자떼어낸뒤, #names Array에추가. print ( names.include? ARGV[1] )? "Yes" : "No"
Hash 기초 학생이름을입력하면점수를알려주기 data = {} # 비어있는 Hash를만듦. data = Hash.new와동일. File.open(ARGV[0], "r:utf-8").each do line name, score = line.chomp.split(",") #line을쉼표로구분 data[name] = score #data에 name-score라는 key-value 쌍을추가 # data[name] 은 data Hash에들어있는 key name과연결된 # value를반환함. print (data.has_key? ARGV[1] )? data[ ARGV[1] ] : " 이름이없습니다."
테이블구조문서처리 : 키, 체중의합, 평균내기 hsum = 0.0 # 키합을저장할변수를실수로초기화 wsum = 0.0 # 체중합을저장할변수를실수로초기화 count = 0 # 학생수를저장할변수를정수로초기화 File.open(ARGV[0], "r:utf-8").each do line name, height, weight = line.chomp.split(",") # 쉼표로분리 height = height.to_f # 문자열을실수로변환 weight = weight.to_f # 문자열을실수로변환 hsum += height # 키합에더함 wsum += weight # 체중합에더함 count += 1 # 학생수 1 증가시킴 print " 키합 : ", hsum, "\n" print " 키평균 : ", hsum/count, "\n" print " 체중합 : ", wsum, "\n" print " 체중평균 : ", wsum/count, "\n"
테이블구조문서처리 : 성적처리 국, 영, 수, 물, 화 5과목총점, 평균추가. 총점순소팅 data = [] # 학생들을요소로하는 Array File.open(ARGV[0], "r:utf-8").each do line name, kor, eng, math, phy, che, job = line.chomp.split(",") sum = kor.to_i + eng.to_i + math.to_i + phy.to_i + che.to_i avg = sum.to_f / 5 student = [name, kor, eng, math, phy, che, sum, avg, job] # 입력의필드 7개와추가된필드 2개를원하는순서대로배열하여학생 1인을나타내는 Array를만듦 data << student # 이학생 object를 data Array에추가 data.sort{ x,y y[6]<=>x[6] }.each do stu #data의각요소의 7번째필드 ( 총점 ) 를기준으로내림차순소팅 stu.each do x # 학생 object를구성하는각필드에대해 print x, "," # 그필드와쉼표를출력 print "\n" # 학생 1인에대한정보출력이끝나면줄바꿈문자출력
다중기준소팅 : 단어등급자료 words.csv: 빈도, 단어, 품사, 의미, 등급 제 1 소팅기준 : 등급, 2: 빈도, 3: 품사, 4: 단어, 5: 의미 dic = [] # 단어 object 들을저장할 Array 를만듦 File.open(ARGV[0], "r:utf-8").each do line freq, form, pos, meaning, grade = line.chomp.split(",") # 쉼표로구분 freq = freq.to_i # 빈도는정수로변환 word = [grade, -freq, pos, form, meaning] # 소팅기준순서대로배열하여하나의 word object 를나타내는 Array 만듦. # 빈도는내림차순소팅을위해 - 추가. dic << word # 이 word object 를 dic 에추가 dic.sort.each do grade, freq, pos, form, meaning #dic 을소팅한뒤 # 각 word 를구성하는필드를변수로지칭가능 print -freq, ",", form, ",", pos, ",", meaning, ",", grade, "\n" # 출력은원순서대로
텍스트빈도통계 <input tr \n sort uniq c sort gr 을 ruby 로구현 data = Hash.new # data = {} 와동일 File.open(ARGV[0], "r:utf-8").each do line line.chomp.split(" ").each do word # 공백으로구분된단어들각각에대해 if data.has_key? word # data 에 word 라는 key 가있으면 data[word] += 1 # 그 word 와연결된수를 1 증가시킴 else # 없으면. 즉이단어를처음만났으면 data[word] = 1 # 그 word 와연결된수를 1 로설정 data.sort{ x,y y[1]<=>x[1] }.each do word, freq # 빈도내림차순으로소팅 print word, ",", freq, "\n"
텍스트빈도통계 2 data = Hash.new(0) #key 의 default value 를 0 으로설정 File.open(ARGV[0], "r:utf-8").each do line line.chomp.split(" ").each do word data[word] += 1 #data 를만들때 default value 를 0 으로설정했으므로 #word 를처음만나는경우에도 # 이와연결된 value 가자동으로 0 으로설정됨. #word 를처음만나든아니든연결된 value 를 1 증가시키면됨 programming idiom: 문법에맞고의미 ( 기능 ) 도동일한여러표현중에서사용자 ( 프로그래머 ) 들이관습적으로흔히사용하는표현 예 : Hash 의 default value 를설정함으로써 key 포함검사를생략할수있게하는것
텍스트빈도통계 3 공백을구분자로하여 split 을하면, Alice, Alice,, Alice., Alice? 등이모두별개의단어로간주됨. 정규표현 /\b/ 를구분자로하면, 일반문자와문장부호사이에단어경계가있으므로 Alice? The 가 Alice,?, The 로구분됨. File.open(ARGV[0], "r:utf-8").each do line line.chomp.split(/\b/).each do word # 정규표현 /\b/ 로구분된단어들각각에대해 data[word] += 1
파일들 join 하기 joined = Hash.new #join 한결과는 { key => [ 각파일의나머지필드들을요소로갖는 Array] } ARGV.each do fn # 사용자가제공한파일이름들에대해 File.open(fn, "r:utf-8").each do line # 파일을열어각라인에대해 key, residue = line.chomp.split(",", 2) # 쉼표를구분자로 split 하되, 2 개로만구분 joined[key] = Array.new #short-circuiting 을이용한 = idiom joined[key] << residue #key 와연결된 Array 에 residue 를추가함. joined.sort.each do key, residues #joined 를 key 로소팅 #residues: 각파일의 residue 들로이루어진 Array 임 print key #key 출력 residues.each do residue #residues Array 의각요소 residue 에대해 print ",", residue # 쉼표와 residue 출력 print "\n" # 하나의 key 에대한 value 의출력이끝나면줄바꿈문자출력
short-circuited evaluation (P and Q), (P && Q) 의값을알아내려할때, P 가 true 이면 Q 의값을 evaluate 해야하나 P 가 false 이면 Q 의값을 evaluate 할필요가없음. (P or Q), (P Q) 의값을알아내려할때, P 가 false 이면 Q 의값을 evaluate 해야하나 P 가 true 이면 Q 는 evaluate 할필요가없음. 연산자에의해결합된두표현의값을다 evaluate 하지않고앞의것만 evaluate 하여전체표현을 evaluate 하는것을 short-circuited evaluation 이라함. boolean 이기대되는자리에서 nil 은 false 로, nil 이외의값은 true 로간주됨. C 언어에서는 0 도 false 로간주됨.
= idiom 어떤변수의값이 nil 인지검사하여, nil 이아니면그냥내버려두고 nil 이면어떤값을할당하는일이흔히있음. 이 logic 을곧이곧대로표현하면 x = a if x == nil 연산자의 short-circuited evaluation 성질을이용하면이것을다음과같이표현할수있음 : x = a 이것을 = idiom 이라함. x =a 는 x = (x a) 와같은의미임. cf. x+=1 은 x=x+1 과같은의미 x 가 non-nil 이면 a 는 evaluate 안함. x=x 이므로아무일도안하는셈 x 가 nil 이면 a 가 evaluate 되고 x=a 가됨. joined[key] = Array.new joined Hash 에 key 가있었으면 (joined[key] 가 nil 이아님 ) 아무일도안함. key 가없었으면, 즉 joined[key] 가 nil 이면, 빈 Array 를만들어이 key 의 value 로삼음.
Array 의 join 메소드 Array 의 join 메소드를이용하면아래코드를더단순화할수있음. residues.each do residue print ",", residue join 메소드는, Array 의각요소 ( 문자열 ) 들을이어붙여하나의문자열로통합하되, 각요소사이에구분자를넣음. [ ab, cd, ef ].join( : ) # ab:cd:ef 구분자를 null string 으로할수도있음. [ ab, cd, ef ].join( ) # abcdef 위의 3 줄의코드는 print,, residues.join(, ) 와동일 학생성적처리할때의아래코드도마찬가지로간략화가능 stu.each do x print x, "," # print stu.join(, )