Ruby 프로그래밍 1 문자열입출력제어구조 looping 함수정의 한국어정보의전산처리 2017. 4. 24.
코드, 컴파일 기계코드 (machine code): 컴퓨터가직접실행할수있는코드. 이진수로되어있어서이진코드 (binary code) 라고도함. 파일로저장할때흔히.exe 라는확장자를가짐. executable 이라고도함. 하드웨어에따라차이가있음. 인간이직접작성하기가매우힘듦. 소스코드 (source code): 인간이이해하고작성하기쉽게고안한코드. 컴파일 (compile): 소스코드를기계코드로바꾸는일. 컴파일러 (compiler): 컴파일해주는소프트웨어 어셈블리코드 (assembly code): 소스코드와기계코드의중간쯤되는코드. 기계코드로 1 대 1 로번역가능한명령들로이루어짐. 어셈블러 (assembler): 어셈블리코드를기계코드로번역해주는소프트웨어.
기계코드와어셈블리코드의예 x86/ia-32 프로세서에게 8 비트값 01100001 을메모리의특정레지스터 AL(000) 로옮기라 (10110) 는기계코드 10110 000 01100001 = B0 61 같은의미를나타내는 8086 family 의어셈블리코드 MOV AL, 61h ( 메모리주소 AL 에 16 진수 61 을로드하라 ) 어셈블리코드는기계코드에직접대응하는명령을사용하기때문에, 전자를후자로매우쉽게번역할수있음. 실행속도가매우빠름. 매크로 (macro) 를이용하여, 복잡한코드를간단하게나타낼수있음. 동일하게반복되는부분은매크로로처리하면효율적. 구조적프로그래밍을가능케함. 인간친화적이아니라, 기계친화적 인간이기계에게익숙한층위까지내려가서프로그래밍하는셈.
컴파일형언어와인터프리트형언어 컴파일형언어 : C, C++, Fortran 인간이소스코드를작성. 컴파일러가이소스코드를기계코드로번역. 실행파일 (executable) 을만들어저장함. 사용자는이실행파일을실행함으로써특정과제를수행함. 컴파일은 1 번만하고, 컴파일결과를여러차례반복실행함. 인터프리트형언어 : Perl, Python, Ruby 인간이소스코드를작성하여 인터프리터가이소스코드를기계코드로번역하여 이기계코드를곧장실행함. ( 실행파일단계를건너뜀 ) 실행할때마다컴파일을다시함. 중간형언어 : Java, C# 인간이작성한소스코드를바이트컴파일러가바이트코드로번역하여저장. 가상기계 (virtual machine) 가바이트코드를기계코드로번역하여실행함. 즉소스코드를절반쯤컴파일한뒤, 나머지절반쯤의컴파일은그때그때함.
각유형의장단점 컴파일형인터프리트형중간형 컴파일비용 컴파일을 1번만하므로비용이적게듦 실행할때마다다시컴파일해야하므로비용이많이듦 절반의컴파일은미리해두고절반의컴파일만그때그때하므로드는비용이중간 실행속도빠름느림중간 실행을위한전제조건 프로그래머의컴퓨터에는컴파일러가있어야하나, 사용자의컴퓨터에는컴파일러가필요없음 사용자의컴퓨터에인터프리터가설치되어있어야함. 비용이가장큼 앞절반의컴파일은프로그래머의컴퓨터에서함. 사용자컴퓨터에는뒤절반의컴파일을해줄가상기계가설치되어있어야함. 소스코드보안 프로그래머는소스코드를감출수있음. 컴파일결과인실행파일만배포하면됨 소스코드를감출수없음. 프로그래머가사용자에게소스코드를배포해야함. 소스코드는감출수있음. 절반쯤컴파일한바이트코드를배포함. 개인적사용시 컴파일, 실행두단계거쳐야하므로번거로움 컴파일단계거치지않고곧장실행하므로간편함 역시컴파일, 실행의두단계를거쳐야하므로번거로움
object 와 type physical object 물리적개체, 물체 시공간상의일정한부분을차지하는존재 (programming) object 프로그래밍개체 컴퓨터메모리상의일정한부분 ( 공간 ) 을차지하는존재 프로그래밍에서가장기초적이고중요한개념 프로그래밍이란, 메모리상에개체를만들고이개체를조작하는일 object-oriented programming 개체 ( 객체 ) 지향프로그래밍 프로그래밍의초기에는개체에주목하지않고기계가수행할연산에주목하다가 점차개체를전면에내세우는프로그래밍언어와기법이주류가됨. 모든개체는일정한 type(class) 에속함. 개체가속하는 type 에따라그개체에대해할수있는일이정해져있음. 문자열 : 프린트하기, 두문자열을이어붙이기, 문자열내에서하위문자열찾기 정수 : 사칙연산 ( 더하기, 빼기, 곱하기, 나누기 ), 나머지, 제곱,... 실수 : 사칙연산, 제곱,... enumerable: 자신이포함한각개체각각에대해어떤일을함 (iterator: each, map, collect 등 )
변수 (variable) 와 type 메모리상에만든개체에대해조작을하고이개체에일어나는변화를추적하려면, 개체를가리키는이름이필요함. 이이름이바로변수임. named object: 변수와연결된, 즉이름을가진개체 unmamed object: 이름을갖지않는개체. literal 이라고도함. 만들어서한번만쓰고말개체는굳이이름을가질필요가없음. 하나의변수가 A 라는개체를가리키는이름으로쓰이다가, 나중에 B 라는개체를가리키는이름으로쓰일수도있음. ( 그래서변수라고함 ) 달리말하면, 변수가가리키는개체의값 (value) 이변할수있음. cf. 상수 (constant): 값이변할수없는개체. immutable 이라고도함. strongly typed language: 개체뿐아니라모든변수가 type 을가짐. 예 :C++ weakly typed language: 개체는 type 을가지나, 변수는 type 을가지지않음. 하나의변수가가리키는개체의 type 이바뀌어도됨. 예 : Ruby
함수 (function) 특정임무를수행하는프로그래밍단위 일정한입력에대해일정한조작을하여일정한출력을내놓음. 함수없이프로그래밍을하다가동일한코드가반복되게된다면, 반복되는부분을함수로독립시킬것을고려해야함. free function: 특정 type 의개체에매여있지않고, 자유롭게호출할수있는함수 class function=method: 특정 type 의개체에대해서만호출할수있는함수 non-mutating method : 개체에변화를초래하지않는 method mutating method : 개체에변화를초래하는 method Ruby 에서 method 명뒤에! 를붙여서구별함.
type system: built-in type Boolean: 참 (true) 과거짓 (false) 의두가지값만가질수있는개체 String: 문자열. 또는 와같이하여만듦. Integer: 정수 Fixnum: 1 (native machine) word 로표시될수있는값을가짐. Bignum: Fixnum 의범위를넘어서는값을가짐. Fixnum 개체에대한어떤연산결과가 Fixnum 의범위를넘으면자동으로 Bignum 으로변환됨 (IRB 를통해예시 ) Float: 실수. 컴퓨터의실수표시메커니즘을이용하여나타냄. 정확하지않음. 루트 2(Math::sqrt(2)) 의제곱이 2.0 이되지않을수도있음. Enumerable: 개체들의집합체 Range: (n..m) n 부터 m 까지의개체들의집합체. 순서가중요함. Array: [a,b,c] 임의의요소들을차례로모아놓은연쇄. 순서가중요함. 상이한 type 에속하는개체들이하나의 Array 에속하는것도가능. Hash: {a=>20, b=>35, c=> xyz } key-value 쌍들을모아놓은집합체. 순서는중요하지않음.
type system: user-defined type 사용자가원하는 type(class) 을정의해서사용할수있음. class Student def initialize(name, serial, age) @name = name @serial = serial @age = age 새로운 type 을정의할때, 반드시 initialize 라는 method 를만들어야함. 이 type 에속하는개체를만들때이 method 가호출됨. 위의 Student class 의경우 a = Student.new( John,372,21) 이라고하면 Student type 에속하는개체가만들어져변수 a 가이개체를가리키며 이개체에딸린 3 개의변수에 John, 372, 21 이라는값이할당됨.
statement 와 expression statement: 어떠어떠한일을하라 는식의완전한문장 a = 10 #a 라는변수에 10 의값을할당하라는 statement print b #b 라는변수에저장되어있는값을출력하라는 statement expression: 하나의 value 로 evaluate 될수있는표현 (a+b)*c #a 와 b 를더하고거기에 c 를곱하여그결과값이얻어짐. Math::sqrt(a) #a 라는변수에저장된값을입력으로하여제곱근함수를적용하여그결과값이얻어짐. x.abs #x 라는변수에저장된값에대해 abs 라는 method 를적용하여그결과값이얻어짐 x == y # 변수 x 에저장된값과변수 y 에저장된값이같다고하는명제. 참또는거짓이라는값으로 evaluate 됨. 프로그래밍을할때둘중어느하나만올수있는위치가있으므로개념적으로구분할필요가있음. 그러나최근에는 statement 도어떤값으로 evaluate 되는경향이있음. 둘의경계가점차모호해지고있음.
제어구조 (control flow): if 문 if 조건 실행할명령들 조건 : 참, 거짓으로 evaluate 될수있는 expression 조건이참일때만블록이실행됨. 실행할명령 if 조건 명령이 1 개일때편리 if 조건 실행할명령들 1 else 실행할명령들 2 if 조건 1 실행할명령들 1 elsif 조건 2 실행할명령들 2 elsif 조건 3 실행할명령들 3 else 실행할명령들 n elsif 블록은얼마든지추가가능 x? y : z #x 가참이면 y, 거짓이면 z 로 evaluate 되는 expression
제어구조 : case 문 case expression when value1 실행할명령들 1 when value2 실행할명령들 2 else 실행할명령들 n case 문은 if ~ elsif ~ else ~ end 형태로치환가능하기는하나 둘중 case 문이더효율적인기계코드로번역되기쉽기때문에, 가급적 case 문을쓸것. case 문은 statement 뿐아니라 expression 에도적용될수있음. 변수 = case expression when value1 expression 1 when value2 expression 2 else expression n expression 의값에따라변수에상이한값할당
반복문 (loop) while 조건 do 실행할명령들 조건이참인동안블록을반복실행 until 조건 do 실행할명령들 조건이거짓인동안블록을반복실행 정수.times do 실행할명령들 블록을 정수 번반복실행 for 변수 in Enumerable do 실행할명령들 Enumerable 안에들어있는각개체를 변수 라는이름으로지칭하면서 이개체에대해명령을실행함. Ruby 에서는같은일을하는더 Ruby 다운 syntax 가있기때문에 for loop 를잘쓰지않음. Enumerable.each do 변수 실행할명령들
새로운개체를만드는방법 new method 를이용하는방법 a = String.new #String type 에속하는비어있는개체, 즉빈문자열을만듦. b = Array.new #Array type 에속하는비어있는개체, 즉빈 Array 를만듦. c = Hash.new #Hash type 에속하는비어있는개체, 즉빈 Hash 를만듦. literal 을변수에할당하는방법 a = 10 # 정수의경우 new 메소드이용불가 b = 2.5 # 실수의경우 new 메소드이용불가 c = "xyz # c = String.new( xyz ) 와같은효과를가짐 d = "" # d = String.new 와같은효과를가짐 e = [] # e = Array.new 와같은효과를가짐 f = {} # f = Hash.new 와같은효과를가짐
unnamed object 의활용 프로그래밍의초보단계에서는, 명령을하나하나실행할때마다만들어지는개체에이름 ( 변수 ) 을부여하는것이좋으나 익숙해짐에따라, 한번사용하고말개체는이름을부여하지않아도됨. 방식 1 x = -5 # 정수개체를만들어그값을 -5 로하고이개체를가리키는변수를 x 로지정 y = x.abs # 변수 x 가가리키는개체에 abs 메소드를적용하여생기는개체를가리키는변수로 y 를지정 print y # 이 y 가가리키는개체를출력 방식 2 print -5.abs # -5 값을갖는정수개체 (unnamed object 1) 를만들어이개체에 abs() 메소드를적용하여생기는개체 (unnamed object 2) 를출력 command line 에서작업의중간결과를파일로저장하지않고여러명령을파이프 로연결하여단번에실행하는것, R 에서파이프 %>% 로여러함수를연결해서사용하는것과비슷한취지
Ruby script 의 shebang Unix/Linux/Cygwin 환경에서인터프리터로실행할 script 의첫라인에서어떤인터프리터를사용할것인지명시해줘야함. 아래의두방식이흔히쓰임. 후자를더권장함. #!/usr/bin/ruby /usr/bin 디렉토리에있는 ruby 라는이름의 executable 을사용하라. #!/usr/bin/env ruby /usr/bin 디렉토리에있는 ruby 라는이름의 executable 을사용하되 만약그디렉토리에 ruby executable 이없으면환경변수 path 에지정된디렉토리들에서찾아보라. ruby script의첫라인에 shebang이있으면, 스크립트파일명만으로실행할수있음 :./script script가들어있는 path/script ruby script 의첫라인에 shebang 이없으면, ruby script 실행시인터프리터를명시해줘야함 : ruby 스크립트파일명
출력 print, puts, 입력 gets, String method Hello! 를출력하는스크립트 print Hello! # Hello! 라는문자열을표준출력장치에출력 사용자로부터이름을입력받아, Hello, 이름! 을출력 print 이름을입력하세요 : name = gets # 사용자가입력한문자열을읽어서 name 변수에저장 # 사용자가타이핑한뒤엔터를치면 gets 가실행됨 # 사용자가마지막에타이핑한줄바꿈문자까지포함됨. name = name.chomp #name 문자열끝의줄바꿈문자를떼어내어다시저장 #name.chomp! 와같은효과 # 위의두줄은 name = gets.chomp 와같은효과 print Hello,, name,! #3 개의문자열을연달아출력 puts 는 print 와같되, 문자열끝에줄바꿈문자를추가하여출력함. 사용자가입력한문자열을뒤집어서출력 : reverse method 이용
command line arguments ruby script 를실행할때 script 파일명뒤에추가로정보를써줄수있는데이를 command line argument 라고함. 공백으로구분된문자열들이 ARGV 라는이름의 Array 에저장됨. 첫째요소는 ARGV[0], 둘째요소는 ARGV[1], 사용자로부터입력받은문자열뒤집어출력 print ARGV[0].reverse 사용자로부터 2 개의문자열을입력받아, 둘째문자열을뒤집어서첫째문자열뒤에붙여출력 print ARGV[0], ARGV[1].reverse 사용자로부터문자열을입력받아그문자열의길이를출력 print ARGV[0].length UTF-8 문자열에서, 각문자가몇바이트를차지하든하나의문자로침.
loop 이용기초 : range 1 부터 100 까지, 한라인에하나씩출력 (1..100).each do i puts i end (1..100).each { i puts i } do ~ end 와 { ~ } 는동일함. 대개 ~ 가 one-liner 일때후자사용 사용자로부터시작값과끝값을입력받아그사이의정수들출력 유의점 : gets 로입력받든 command line argument 로입력받든사용자로부터입력받은데이터는기본적으로 String 임. 필요한경우 type 변환을해야함. x.to_i #x 의 type 을정수 (integer) 로변환함. 크기에따라 Fixnum, Bignum 이자동적으로결정됨. x.to_f #x 의 type 을실수 (floating number) 로변환함. x.to_s #x 의 type 을문자열 (string) 로변환함. (ARGV[0].to_i..ARGV[1].to_i).each { i puts i } 문자열 range 도가능. 사용자로부터입력받은시작문자열, 끝문자열사이의문자열들출력하기 : (ARGV[0]..ARGV[1]).each { s puts s }
loop 내에서 if 문으로조건검사 사용자로부터입력받은정수 range 내에서짝수만출력 (ARGV[0].to_i..ARGV[1].to_i).each do i puts i if i % 2 == 0 # i 를 2 로나눈나머지가 0 이면 i 출력. i.even? 을써도됨 홀수만출력하기, 3 의배수만출력하기 i % 2 == 1 또는 i.odd? i % 3 == 0 제곱근이정수인것 ( 정수의제곱인것 ) 만출력하기 (ARGV[0].to_i..ARGV[1].to_i).each do i sqrt_of_i = Math::sqrt(i) #Math::sqrt 함수를이용하여 i 의제곱근을구함. 결과값은실수. puts i if sqrt_of_i == sqrt_of_i.to_i # 제곱근결과값에.to_i method 를적용 ( 소수점이하를버림 ) 한결과가제곱근자체와같으면, 즉소수점이하가 0 이면 i 출력 # 방법 1 i = ARGV[0].to_i # 첫째 command line argument( 시작값 ) 를정수로변환하여 i 에저장 while i**2 <= ARGV[1].to_i do # until i**2 > ARGV[1].to_i 와같음. # i 의제곱이상한선이하이면아래블록을반복실행함. puts i**2 # i 의제곱을출력 i += 1 # i = i +1 과같음. i 의값을 1 증가시킴. # 방법 2
더하기, 평균구하기 사용자로부터입력받은시작값부터끝값까지의정수들의합, 평균구하기 sum = 0 # 정수들을합을저장할변수 sum 을 0 으로초기화 range = (ARGV[0].to_i..ARGV[1].to_i) # 하한선.. 상한선 range 를저장 range.each do i #range 내의각정수 i 에대해 sum += i # sum = sum + i 와동일. sum 에 i 를더함. print sum, \t, sum.to_f / range.size #sum 과평균을출력 평균을낼때 sum 을우선실수로변환해야함에유의할것. 정수와정수의연산은결과값도정수가됨. ( 소수점이하버림 ) 실수와실수의연산은결과값도실수가됨. 실수와정수의연산은결과값이실수가됨. 같은 type 에속하는개체들끼리의연산은결과값도같은 type 의개체가됨. 정보량이많은 type 의개체와정보량이적은 type 의개체사이에연산을할때, 후자가전자로자동으로변환되어연산을수행함.
사용자로부터입력받은정수들의합, 평균 1 print " 정수들의합, 평균을구해드립니다. 입력할정수의개수 : " n = gets.chomp.to_i # 입력할정수의개수를 n 에저장 sum = 0 # 합을저장할변수 sum 을 0 으로초기화 n.times do # 아래의블록을 n 번반복실행 i = gets.chomp.to_i # 사용자가입력한것을정수로변환, i 에저장 sum += i #sum 에 i 를더함 # 위의두줄은 sum+=gets.chomp.to_i 라고할수도있음. unnamed object 이용. print sum, \t, sum.to_f / n # 합과평균출력
사용자로부터입력받은정수들의합, 평균 2 print " 정수들의합, 평균을구해드립니다. 입력할정수의개수 : " n = gets.chomp.to_i a = [] # a = Array.new와같음. 빈 Array를만듦. n.times do i = gets.chomp.to_i a << i #Array a에 i를추가 sum = a.inject(:+) # + 연산자를이용하여 a의요소들을집계 #ruby 2.4 이상에서는 a.sum도가능 print sum, "\t", sum.to_f / a.length
소수 (prime number) 판정 사용자로부터상한선을입력받아, 2부터상한선사이의소수출력 (2..ARGV[0].to_i).each do i #2부터사용자가제공한상한선까지 is_prime = true #is_prime 변수를일단참으로초기화 (2..i/2).each do x #2부터 i/2( 정수 ) 까지, 각 x에대해 if i % x == 0 #i를 x로나눈나머지가 0이면 is_prime = false # is_prime 변수를거짓으로하고 break # 가장하위의 loop를종료 puts i if is_prime #is_prime이참이면 i를출력
소수판정함수 어떤정수가소수인지판정하는부분을별도의함수로독립시키면프로그램의 logic 이더명확해짐. prime_func.rb (called code) def is_prime?(i) is_prime = true (2..i/2).each do x if i % x == 0 is_prime = false break is_prime # is_prime 의값반환 prime2.rb (calling code) #!/usr/bin/env ruby require './prime_func.rb # 호출할함수를정의한파일을 require 로불러옴. (2..ARGV[0].to_i).each do i puts i if is_prime?(i) #is_prime? 함수를사용