-
Notifications
You must be signed in to change notification settings - Fork 0
/
02-var.qmd
1121 lines (839 loc) · 33.2 KB
/
02-var.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
editor:
markdown:
wrap: sentence
---
```{r}
#| include: false
source("_common.R")
```
# 변수, 표현식, 문장 {#r-var}
## 값과 자료형 {#r-var-value-type}
\index{값}
\index{자료형}
\index{문자열}
**값(Value)**은 문자와 숫자처럼 프로그램이 다루는 가장 기본이 되는 단위이다.
지금까지 살펴본 값은 1, 2 그리고 '헬로 월드!' 이다.
상기 값은 다른 **자료형(Type)**에 속하는데, 2는 **정수(integer)**, '헬로 월드!' 는 **문자열(String)**에 속한다.
문자(character)가 일련의 열(sequence) 형태로 되어 있어서 문자열이라고 부른다.
인용부호에 감싸여 있어서, 여러분과 인터프리터는 문자열을 식별할 수 있다.
\index{인용부호}
`print` 문은 정수에도 사용할 수 있다.
R 명령어를 실행하여 인터프리터를 구동시키자.
::: panel-tabset
### R
```{webr-r}
#| label: r-var-print
print(7)
#> [1] 7
```
### 파이썬
```{pyodide-python}
#| label: py-var-print
print(7)
#> 7
```
:::
값이 어떤 형인지 확신을 못한다면, 인터프리터가 알려준다.
::: panel-tabset
### R
```{webr-r}
#| label: r-var-type
typeof("헬로 월드!")
#> [1] "character"
typeof(17)
#> [1] "double"
```
### 파이썬
```{pyodide-python}
#| label: py-var-type
print(type("헬로 월드!"))
#> <class 'str'>
print(type(17))
#> <class 'int'>
```
:::
다소 놀랄 수도 있겠지만, `17`은 **부동소수점** 숫자 형식 더블(double)이고 `헬로 월드!`는 문자(character)가 된다.
```{r}
#| label: r-var-integer
typeof(7L)
```
정수형을 필히 표현하려면 `7L`와 같이 정수 뒤에 `L`을 붙이면 된다.
'17', '3.2' 같은 값은 어떨까? 숫자처럼 보이지만 문자열처럼 인용부호에 감싸여 있다.
```{r}
#| label: r-var-integer-character
typeof('17L')
typeof('3.2')
```
'17', '3.2'은 문자열이다.
\index{자료형}
\index{문자열 자료형}
\index{자료형!문자열 자료형}
\index{정수형}
\index{자료형!정수형}
\index{부동소수점형}
\index{자료형!부동소수점형}
\index{type}
\index{type!str}
\index{type!int}
\index{type!float}
1,000,000처럼 아주 큰 정수를 입력할 때, 사람이 인식하기 편한 형태로 세 자리 숫자마다 콤마(,)를 사용하고 싶을 것이다.
하지만, R에서는 오류가 난다.
::: panel-tabset
### R
```{webr-r}
#| label: r-var-integer-comma
1,000,000
#> Error: unexpected ',' in "1,"
```
### 파이썬
```{pyodide-python}
#| label: py-var-integer-comma
1,000,000
#> (1, 0, 0)
```
:::
파이썬의 경우 정상적으로 실행되나, 실행 결과는 우리가 기대했던 것이 아니다.
파이썬에서는 1,000,000을 콤마(',')로 구분된 정수로 인식한다.
따라서 사이사이 공백을 넣어 출력했다.
이 사례가 여러분이 처음 경험하게 되는 **의미론적 오류(semantic error)**이다.
코드가 에러 메시지 없이 실행되지만, "올바른(right)" 작동을 하는 것은 아니다.
\index{의미론적 오류}
\index{오류!의미론}
\index{오류!오류 메시지}
\index{변수}
\index{대입문}
\index{문장!대입)}
## 변수 {#r-var-variable}
프로그래밍 언어의 가장 강력한 기능 중 하나는 변수를 다룰 수 있는 능력이다.
**변수(Variable)**는 값을 참조하는 이름이다.
**대입문(Assignment statement)**은 새로운 변수를 생성하고 값을 변수에 대입한다.
::: panel-tabset
### R
```{webr-r}
#| label: r-var-assign
message <- "매월 셋째 주 수요일 R Meetup이 열립니다."
n <- 17L
pi <- 3.1415926535897931
```
### 파이썬
```{pyodide-python}
#| label: py-var-assign
message = "매월 셋째 주 수요일 R Meetup이 열립니다."
n = 17
pi = 3.1415926535897931
```
:::
상기 예제는 세 가지 대입 사례를 보여준다.
첫 번째 대입 예제는 `message` 변수에 문자열을 대입한다.
두 번째 예제는 변수 `n`에 정수 17을 대입한다.
세 번째 예제는 `pi` 변수에 근사값을 대입한다.
변수 값을 출력하기 위해서 `print` 문을 사용해도 되지만, 일반적으로 변수명을 콘솔에서 타이핑하면 된다.
::: panel-tabset
### R
```{webr-r}
#| label: r-var-assign-again
print(message)
n
pi
```
### 파이썬
```{pyodide-python}
#| label: py-var-assign-again
print(message)
print(n)
print(pi)
```
:::
변수 자료형(type)은 변수가 참조하는 값의 자료형이다.
::: panel-tabset
### R
```{webr-r}
#| label: r-var-assign-typeof
typeof(message)
typeof(n)
typeof(pi)
```
### 파이썬
```{pyodide-python}
#| label: py-var-assign-typeof
type(message)
type(n)
type(pi)
```
:::
## 변수명과 예약어 {#r-var-keywords}
\index{예약어} \index{keyword}
\index{밑줄}
대체로 프로그래머는 의미 있는 **변수명(variable name)**을 고른다.
프로그래머는 변수가 사용되는 것에 대해 문서화도 한다.
변수명은 임의로 길 수 있다.
변수명은 문자와 숫자를 포함할 수 있지만, 문자로 변수명을 시작해야 한다.
첫 변수명을 대문자로 사용해도 되지만 소문자로 변수명을 시작하는 것도 좋은 생각이다.
(후에 왜 그런지 보게 될 것이다.)
변수명에 밑줄(underscore character, \_)이 들어갈 수 있다.
종종 `my_name` 혹은 `airspeed_of_unladen_swallow`처럼 밑줄은 여러 단어와 함께 사용된다.
변수명을 밑줄로 시작해서 작성할 수 있지만, 다른 사용자가 사용할 라이브러리를 작성하는 경우가 아니라면, 일반적으로 밑줄로 시작하는 변수명은 피한다.
한글을 변수명으로 사용하는 것도 가능하지만, 인코딩 등 여타 예기치 못한 문제가 생길 수도 있다는 점을 유념하고 사용한다.
R이 다른 언어와 다른 점은 `<-`을 변수명에 값을 대입하는 데 사용하는 점이다.
이유는 R이 한창 개발될 당시 가장 최신 이론에 바탕을 두고 있기 때문이다.
수학적으로 `variable_name = 123L`와 같은 문장이 맞는지 곰곰이 생각해 보면 그 당시 `<-` 기호를 사용한 이유를 유추할 수 있다.
\index{예약어}
변수명을 적합하게 작성하지 못하면, 구문 오류가 발생한다.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-assign-error
76trombones <- 'big parade'
#> Error: <text>:1:3: unexpected symbol
more@ <- 1000000
#> Error: <text>:1:3: unexpected symbol
repeat <- 'Advanced Theoretical Zymurgy'
#> Error: <text>:1:3: unexpected symbol
```
### 파이썬
```{pyodide-python}
#| label: py-var-assign-error
76trombones = 'big parade'
#> SyntaxError: invalid syntax
more@ = 1000000
#> SyntaxError: invalid syntax
class = 'Advanced Theoretical Zymurgy'
#> SyntaxError: invalid syntax
```
:::
`76trombones` 변수명은 문자로 시작하지 않아서 적합하지 않다.
`more@`은 특수 문자(\@)를 변수명에 포함해서 적합하지 않다.
하지만, `repeat` 변수명은 뭐가 잘못된 것일까?
구문 오류 이유로 `repeat`이 R의 예약어 중 하나라는 것이 밝혀졌다.
인터프리터가 예약어를 사용하여 프로그램 구조를 파악하기 위해서 사용하지만, 변수명으로는 사용할 수 없다.
예약어는 프로그래밍 언어에서 특별한 목적으로 사용되는 단어다.
[@tbl-r-keywords] [@tbl-py-keywords]에 R과 파이썬 예약어가 기능별로 비교되어 있다.
:::{.panel-tabset}
### R
| R 예약어 | 설명 |
|----------------|--------------|
| if, else, repeat, while, for, in, next, break | 조건문 및 반복문 제어 |
| function, ... (ellipsis) | 함수 관련 |
| TRUE, FALSE, NULL, Inf, NaN, NA, NA_integer_, NA_real_, NA_complex_, NA_character_ | 논리 및 상수 |
| <- (assignment), -> | 변수 할당 |
| $, :, ::, ::: | 객체 및 패키지 접근 |
| ^, +, -, *, / | 산술 연산자 |
| >, <, ==, !=, <=, >= | 비교 연산자 |
| !, &, \|, &&, \|\| | 논리 연산자 |
| %any%, %in% | 특별 목적 연산자 |
| {, } | 코드 블록 정의 |
: R 예약어 {#tbl-r-keywords}
### 파이썬
| 파이썬 예약어 | 설명 |
|----------------|------------------|
| False, True | 논리 상수(Logical constants) |
| break, continue, for, while | 반복문 제어 |
| def, return, lambda | 함수 정의 |
| class | 클래스 정의 |
| if, elif, else | 조건문 |
| try, except, finally, raise | 예외 처리 |
| global, nonlocal | 변수 범위 |
| import, from, as | 모듈 관련 |
| and, or, not | 논리 연산자 |
| assert, del, pass, yield, in, is, None | 기타 |
: 파이썬 예약어 {#tbl-py-keywords}
:::
이 표는 R의 예약어 및 특수 문자들을 포함하고 있으며, 각각의 용도나 기능에 대해 간단한 설명을 제공한다.
R에서는 이러한 예약어와 특수 문자들을 변수명이나 함수명으로 사용하는 것을 피해야 한다.
상기 예약어 목록을 주머니에 넣고 잘 가지고 다니고 싶을 것이다.
만약 인터프리터가 변수명 중 하나에 대해 불평을 하지만 이유를 모르는 경우, 예약어 목록에 변수명이 있는지 확인해 보자.
## 문장 {#r-var-statement}
\index{인터랙티브 모드}
\index{스크립트 모드}
**문장(statement)**은 R 인터프리터가 실행하는 코드 단위다.
지금까지 `print`, `대입`(assignment, `<-`) 두 종류의 문장을 살펴보았다.
인터랙티브 모드에서 문장을 입력하면, 인터프리터는 문장을 실행하고, 만약 출력할 것이 있다면 결과를 화면에 출력한다.
스크립트는 보통 여러 줄의 문장으로 구성된다.
하나 이상의 문장이 있다면, 문장이 순차적으로 실행되며 결과가 한 번에 하나씩 나타난다.
예를 들어, 다음의 스크립트를 생각해 보자.
스크립트는 다음 결과를 출력한다.
::::: {.columns}
::: {.column width="47.5%"}
```{r}
#| label: r-var-statement
#| eval: false
1
x <- 2
x
```
:::
::: {.column width="5%"}
:::
::: {.column width="47.5%"}
```{r}
#| label: r-var-statement-eval
#| eval: true
1
x <- 2
x
```
:::
:::::
대입 문장(`x <- 2`)은 결과를 출력하지 않는다.
## 연산자와 피연산자 {#r-var-operator}
\index{연산자, 산술}
\index{산술 연산자}
\index{피연산자}
\index{표현식}
**연산자(Operators)**는 덧셈, 곱셈 같은 계산(Computation)을 표현하는 특별한 기호다.
연산자가 적용되는 값을 **피연산자(operands)**라고 한다.
다음 예제와 같이, +, -, \*, /, \*\* 연산자는 각각 덧셈, 뺄셈, 곱셈, 나눗셈 및 지수 연산을 실행한다.
`20+32`
`hour-1`
`hour*60+minute`
`minute/60`
`5**2 (5+9)*(15-7)`
\index{파이썬 3}
\index{버림 나눗셈}
\index{부동 소수점 나눗셈}
\index{나눗셈!절사}
\index{나눗셈!부동 소수점}
## 표현식 {#r-var-expression}
\index{표현식}
\index{평가}
**표현식(expression)**은 값, 변수, 연산자 조합이다.
값은 그 자체로 표현식이고, 변수도 동일하다.
따라서 다음 표현식은 모두 적합하다.
(변수 x는 사전에 어떤 값이 대입되었다고 가정한다.)
```{r}
#| label: r-var-expression
#| eval: false
17
x
x + 17
```
인터랙티브 모드에서 표현식을 입력하면, 인터프리터는 표현식을 **평가(evaluate)**하고 값을 표시한다.
```{r}
#| label: r-var-evaluate
1 + 1
```
하지만, 스크립트에서는 표현식 자체로 어떠한 것도 수행하지 않는다.
초심자에게 혼란스러운 점이다.
**연습문제.** R 인터프리터에 다음 문장을 입력하고 결과를 보자.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-evaluate-ex
5
x <- 5
x + 1
```
### 파이썬
```{pyodide-python}
#| label: py-var-evaluate-ex
5
x = 5
x + 1
```
:::
## 연산자 적용 우선순위 {#r-var-order-operations}
\index{연산자 적용 우선순위}
\index{우선순위 규칙}
\index{PEMDAS}
1개 이상의 연산자가 표현식에 등장할 때,
연산자 평가 순서는 **우선순위 규칙(rules of precedence)**에 따른다.
수학 연산자에 대해서 파이썬은 수학적 관례를 동일하게 따른다.
영어 두문어 **PEMDAS**는 기억하기 좋은 방식이다.
\index{괄호!최우선 우선순위}
- **괄호(Parentheses)**는 가장 높은 순위를 가지고 여러분이 원하는 순서에 맞춰 실행할 때 사용한다. 괄호 내의 식이 먼저 실행되기 때문에 `2 * (3-1)`은 4가 정답이고, `(1+1)**(5-2)`는 8이다. 괄호를 사용하여 표현식을 좀 더 읽기 쉽게 하려고 사용하기도 한다. `(minute * 100) / 60`는 실행순서가 결과값에 영향을 주지 않지만 가독성이 상대적으로 더 좋다.
- **지수승(Exponentiation)**이 다음으로 높은 우선순위를 가진다. 그래서 `2**1+1`는 4가 아니라 3이고, `3*1**3`는 27이 아니고 3이다.
- **곱셈(Multiplication)과 나눗셈(Division)**은 동일한 우선순위를 가지지만, 덧셈(Addition), 뺄셈(Subtraction)보다 높은 우선순위를 가진다. 덧셈과 뺄셈은 같은 실행 우선순위를 갖는다. `2*3-1`는 4가 아니고 5이고, `6+4/2`는 5가 아니라 8이다.
- 같은 실행 순위를 갖는 연산자는 왼쪽에서부터 오른쪽으로 실행된다. `5-3-1` 표현식은 3이 아니고 1이다. 왜냐하면 `5-3`이 먼저 실행되고 나서 2에서 1을 빼기 때문이다.
여러분이 의도한 순서대로 연산이 수행될 수 있도록, 좀 의심스러운 경우는 항상 괄호를 사용한다.
## 나머지 연산자 {#r-var-modular}
\index{나머지 연산자}
\index{연산자!나머지}
**나머지 연산자(modulus operator)**는 정수에 사용하며, 첫 번째 피연산자를 두 번째 피연산자로 나눌 때 나머지 값이 생성된다.
파이썬에서 나머지 연산자는 퍼센트 기호(%)이다.
구문은 다른 연산자와 동일하다.
7을 3으로 나누면 몫이 2가 되고 나머지가 1이 된다.
나머지 연산자가 놀랍도록 유용하다.
예를 들어 한 숫자를 다른 숫자로 나눌 수 있는지 없는지를 확인할 수도 있다.
`x %% y` 값이 0이라면, x를 y로 나눌 수 있다.
또한, 숫자에서 가장 오른쪽 숫자를 분리하는 데도 사용된다.
예를 들어 `x %% 10`은 x가 10진수인 경우 가장 오른쪽 숫자를 뽑아낼 수 있고, 동일한 방식으로 `x %% 100`은 가장 오른쪽 2개 숫자를 뽑아낼 수도 있다.
나눗셈 연산자의 경우 `minute` 값은 59, 보통 59를 60으로 나누면 0 대신에 0.98333이다.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-division
minute <- 59
minute / 60
```
### 파이썬
```{pyodide-python}
#| label: py-var-division}
minute = 59
minute / 60
```
:::
\index{가분성}
하지만, 몫을 `minute %/% 60`와 같이 계산하여 `r minute <- 59; minute %/% 60`을 얻고, 나머지를 `minute %% 60`와 같이 계산하여 `r minute <- 59; minute %% 60`을 얻게 된다.
## 문자열 연산자 {#r-var-string-operator}
\index{문자열!연산}
\index{연산자!문자열}
`+` 연산자는 문자열에는 동작하지 않는다.
대신에 문자열 끝과 끝을 붙이는 **연결(concatenation)** 작업을 수행할 때 `paste()` 함수를 사용한다.
예를 들어,
\index{연결}
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-paste
first <- 10
second <- 15
first + second
first <- '100'
second <- '150'
paste(first, second, sep="")
```
### 파이썬
```{pyodide-python}
#| label: py-var-paste
first = 10
second = 15
first + second
first = '100'
second = '150'
print(first + second)
```
:::
상기 프로그램 출력은 `100150`이다.
## 입력값 받기 {#r-var-input}
\index{키보드 입력}
때때로 키보드를 통해서 사용자로부터 변수에 대한 값을 입력받고 싶을 때가 있다.
키보드로부터 입력값을 받는 `readline()`이라는 내장(built-in) 함수를 R에서 제공한다.
입력 함수가 호출되면, R은 실행을 멈추고 사용자가 무언가 입력하기를 기다린다.
사용자가 Return(리턴) 혹은 Enter(엔터) 키를 누르게 되면 프로그램이 다시 실행되고, `readline()`은 사용자가 입력한 것을 문자열로 반환한다.
\index{readline 함수}
\index{함수!readline}
:::{.panel-tabset}
### R
```{r}
#| label: r-var-user-input
#| eval: false
user_input <- readline()
#> R은 사랑입니다.
user_input
#> [1] "R은 사랑입니다."
```
### 파이썬
```{python}
#| label: py-var-user-input
#| eval: false
user_input = input()
#> 파이썬은 사랑입니다.
print(user_input )
#> "파이썬은 사랑입니다."
```
:::
사용자로부터 입력받기 전에 프롬프트에서 사용자가 어떤 값을 입력해야 하는지 정보를 제공하는 것도 좋은 생각이다.
입력을 받기 위해 잠시 멈춰있을 때, 사용자에게 표시되도록 readline() 함수에 문자열을 전달할 수 있다.
\index{프롬프트}
:::{.panel-tabset}
### R
```{r}
#| label: r-var-user-input-print
#| eval: false
user_input <- readline(prompt="아무거나 입력하시오: ")
#> 아무거나 입력하시오: R은 사랑입니다.
user_input
#> [1] "R은 사랑입니다."
```
### 파이썬
```{python}
#| label: py-var-user-input-print
#| eval: false
user_input = input("아무거나 입력하시오: ")
#> 아무거나 입력하시오: 파이썬은 사랑입니다.
print(user_input )
#> "파이썬은 사랑입니다."
```
:::
경우에 따라서 프롬프트의 끝에 `\n`을 넣는 경우도 있는데 **줄바꿈(newline)**을 의미한다.
줄바꿈은 줄을 바꾸게 하는 특수 문자다.
사용자 입력이 프롬프트 밑에 출력되도록 줄바꿈이 필요한 경우 사용한다.
\index{줄바꿈}
만약 사용자가 정수를 입력하기를 바란다면,
`int()` 함수를 사용하여 반환되는 값을 정수(int)로 자료형을 변환한다.
:::{.panel-tabset}
### R
```{r}
#| label: r-var-prompt
#| eval: false
prompts <- '속도가 얼마나 됩니까? '
speed <- readline(prompt=prompts)
#> 속도가 얼마나 됩니까? 20
as.integer(speed) + 5
#> [1] 25
```
### 파이썬
```{python}
#| label: py-var-prompt
#| eval: false
prompt = '속도가 얼마나 됩니까? '
speed = input(prompt)
#> 속도가 얼마나 됩니까? 20
result = int(speed) + 5
print(result)
#> 25
```
:::
하지만, 사용자가 숫자 문자열이 아닌 다른 것을 입력하게 되면 오류가 발생한다.
:::{.panel-tabset}
### R
```{r}
#| label: r-var-prompt-error
#| eval: false
prompts <- '속도가 얼마나 됩니까? '
speed <- readline(prompt=prompts)
#> 속도가 얼마나 됩니까? 뭐라고 하셨나요!!!
as.integer(speed) + 5
#> [1] 25
```
### 파이썬
```{python}
#| label: py-var-prompt-error
#| eval: false
prompt = '속도가 얼마나 됩니까? '
speed = input(prompt)
#> 속도가 얼마나 됩니까? 뭐라고 하셨나요!!!
result = int(speed) + 5
print(result)
#> Traceback (most recent call last):
#> File "C:/Users/statkclee/AppData/Local/Programs/Python/Python311/book.py", line 9, in <module>
#> result = int(speed) + 5
#> ValueError: invalid literal for int() with base 10: '뭐라고 하셨나요!!!'
```
:::
\index{값오류}
\index{예외!값오류}
나중에 이런 종류의 오류를 어떻게 다루는지 배울 것이다.
## 주석 {#r-var-comment}
\index{주석}
프로그램이 커지고 복잡해짐에 따라 가독성은 떨어진다.
형식 언어(formal language)는 촘촘하고 코드 일부분도 읽기 어렵고 무슨 역할을 왜 수행하는지 이해하기 어렵다.
이런 이유로 프로그램이 무엇을 하는지를 자연어로 프로그램에 노트를 달아두는 것은 좋은 생각이다.
이런 노트를 **주석(Comments)**이라고 하고 `#` 기호로 시작한다.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-comment
# 경과한 시간을 퍼센트로 계산
percentage <- (minute * 100) / 60
```
### 파이썬
```{pyodide-python}
#| label: py-var-comment
# 경과한 시간을 퍼센트로 계산
percentage = (minute * 100) / 60
```
:::
상기 사례의 경우, 주석 자체가 한 줄이다.
주석을 프로그램의 맨 뒤에 놓을 수도 있다.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-comment-inline
percentage <- (minute * 100) / 60 # 경과한 시간을 퍼센트로 계산
```
### 파이썬
```{pyodide-python}
#| label: py-var-comment-inline
percentage = (minute * 100) / 60 # 경과한 시간을 퍼센트로 계산
```
:::
뒤의 모든 것은 무시되기 때문에 프로그램에는 아무런 영향이 없다.
명확하지 않은 코드의 기능을 문서화할 때 주석은 가장 유용하게 된다.
프로그램을 읽는 사람이 코드가 무엇을 하는지 이해한다고 가정하는 것은 일리가 있다.
왜 그런지를 이유를 설명하는 것은 더욱 유용하다.
다음의 주석은 코드와 중복되어 쓸모가 없다.
```{r}
#| label: r-var-comment-useless
v <- 5 # 5를 v에 대입
```
다음의 주석은 코드에 없는 유용한 정보가 있다.
```{r}
#| label: r-var-comment-useful
v <- 5 # 미터/초 단위로 측정된 속도
```
좋은 변수명은 주석을 할 필요를 없게 만들지만,
지나치게 긴 변수명은 읽기 어려운 복잡한 표현식이 될 수 있다.
그래서 상충관계(trade-off)가 존재한다.
## 연상되는 변수명 {#r-var-name}
\index{연상기호}
변수를 이름 짓는 데 단순한 규칙을 지키고 예약어를 피하기만 한다면, 변수 이름을 작명할 수 있는 경우의 수는 무척이나 많다.
처음에 이렇게 넓은 선택폭이 오히려 프로그램을 읽는 사람이나 프로그램을 작성하는 사람 모두에게 혼란을 줄 수 있다.
예를 들어, 다음의 3개 프로그램은 각 프로그램이 달성하려는 관점에서 동일하지만, 여러분이 읽고 이해하는 데는 많은 차이점이 있다.
```{r}
#| label: r-var-name
a <- 35.0
b <- 12.50
c <- a * b
print(c)
hours <- 35.0
rate <- 12.50
pay <- hours * rate
print(pay)
x1q3z9ahd <- 35.0
x1q3z9afd <- 12.50
x1q3p9afd <- x1q3z9ahd * x1q3z9afd
print(x1q3p9afd)
```
R 인터프리터는 상기 3개 프로그램을 정확하게 동일하게 바라보지만, 사람은 이들 프로그램을 매우 다르게 보고 이해한다.
사람은 가장 빨리 두 번째 프로그램의 **의도**를 알아차린다.
왜냐하면 각 변수에 무슨 데이터가 저장될지에 관해서, 프로그래머의 **의도**를 반영하는 변수명을 사용했기 때문이다.
현명하게 선택된 변수명을 연상기호 변수명("mnemonic variable name")이라고 한다.
연상되기 좋은 영어 단어(["mnemonic"](http://en.wikipedia.org/wiki/Mnemonic))는 기억을 돕는다는 뜻이다.
왜 변수를 생성했는지 기억하기 좋게 하기 위해서 연상하기 좋은 변수명을 선택한다.
매우 훌륭하게 들리고, 연상하기 좋은 변수명을 만드는 것이 좋은 아이디어 같지만,
기억하기 좋은 변수명은 초보 프로그래머가 코드를 파싱(parsing)하고 이해하는 데 걸림돌이 되기도 한다.
왜냐하면 얼마 되지 않는 예약어도 기억하지 못하고, 변수명이 때때로 너무 서술적이라 마치 일반적으로 사용하는 언어처럼 보이고 잘 선택된 변수명처럼 보이지 않기 때문이다.
어떤 데이터를 반복하는 다음 파이썬 코드를 살펴보자. 곧 반복 루프를 살펴보겠지만, 다음 코드가 무엇을 의미하는지 알기 위해서 퍼즐을 풀어보자.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-name-for-loop-season
words <- c("봄", "여름", "가을", "겨울")
for(word in seq_along(words)) {
print(words[word])
}
```
### 파이썬
```{pyodide-python}
#| label: py-var-name-for-loop-season
words = ["봄", "여름", "가을", "겨울"]
# Python의 for 루프 사용
for word in words:
print(word)
```
:::
무엇이 일어나고 있는 것일까? for, word, in 등등 어느 토큰이 예약어일까? 변수명은 무엇일까?
파이썬은 기본적으로 단어의 개념을 이해할까? 초보 프로그래머는 어느 부분 코드가 이 예제와 동일해야만 하는지 그리고,
어느 부분 코드가 프로그래머 선택에 의한 것인지 분간하는 데 고생을 한다.
다음의 코드는 위의 코드와 동일하다.
::::: {.columns}
::: {.column width="47.5%"}
### R {.unnumbered}
```{r}
#| label: r-var-name-for-loop
#| eval: false
for(slice in seq_along(pizza)) {
print(pizza[slice])
}
```
:::
::: {.column width="5%"}
:::
::: {.column width="47.5%"}
### 파이썬 {.unnumbered}
```{python}
#| label: py-var-name-for-loop
#| eval: false
for slice in pizza:
print(slice)
```
:::
:::::
초보 프로그래머가 이 코드를 보고 어떤 부분이 R 예약어이고 어느 부분이 프로그래머가 선택한 변수명인지 알기 쉽다.
R이 피자와 피자조각에 대한 근본적인 이해가 없고, 피자는 하나 혹은 여러 조각으로 구성된다는 근본적인 사실을 알지 못한다는 것은 자명하다.
하지만, 작성한 프로그램이 데이터를 읽고 데이터에 있는 단어를 찾는다면 피자(pizza)와 피자조각(slice)은 연상하기 좋은 변수명이 아니다.
이것을 변수명으로 선택하게 되면 프로그램의 의미를 왜곡시킬 수 있다.
좀 시간을 보낸 후에 가장 흔한 예약어에 대해서 알게 될 것이고, 이들 예약어가 어느 순간 여러분에게 눈에 띄게 될 것이다.
::::: {.columns}
::: {.column width="47.5%"}
### R {.unnumbered}
```{r}
#| label: r-var-name-for-loop2
#| eval: false
for(slice in seq_along(pizza)) {
print(pizza[slice])
}
```
:::
::: {.column width="5%"}
:::
::: {.column width="47.5%"}
### 파이썬 {.unnumbered}
```{python}
#| label: py-var-name-for-loop2
#| eval: false
for slice in pizza:
print(slice)
```
:::
:::::
R에서 정의된 코드 일부분(**for**, **in**, **print**)은 예약어로 굵게 표시되어 있고,
프로그래머가 생성한 변수명(word, words)은 굵게 표시되어 있지 않다.
대다수 텍스트 편집기는 R 구문을 인지하고 있어서, R 예약어와 프로그래머가 작성한 변수를 구분하기 위해서 색깔을 다르게 표시한다.
잠시 후에 여러분은 R을 읽고 변수와 예약어를 빠르게 구분할 수 있을 것이다.
## 디버깅 {#r-var-debug}
\index{디버깅}
이 지점에서 여러분이 저지르기 쉬운 구문 오류는 `odd~job`, `US$` 같은 특수문자를 포함해서 잘못된 변수명을 생성하는 것과 `repeat`, `while` 같은 예약어를 변수명으로 사용하는 것이다.
\index{구문 오류}
\index{오류!구문}
변수명에 공백을 넣는다면, R은 연산자 없는 두 개의 피연산자로 생각한다.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-bad-name
bad name <- 5
#> Error: unexpected symbol in "bad name"
```
### 파이썬
```{pyodide-python}
#| label: py-var-bad-name
bad name = 5
#> SyntaxError: invalid syntax
```
:::
구문 오류에 대해서, 오류 메시지는 그다지 도움이 되지 못한다.
가장 흔한 오류 메시지는 `Error: unexpected symbol in "bad name"`인데 둘 다 그다지 오류에 대한 많은 정보를 주지는 못한다.
\index{오류 메시지}
\index{정의 전 사용}
\index{예외}
\index{런타임 오류}
\index{오류!런타임}
여러분이 많이 범하는 실행 오류는 정의 전에 사용("use before function/def")하는 것으로 변수에 값을 대입하기 전에 변수를 사용할 경우 발생한다.
주로 변수명을 잘못 쓸 때도 발생할 수 있다.
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-use-before-def
principal <- 327.68
interest <- principle * rate
#> Error: object 'principle' not found
```
### 파이썬
```{pyodide-python}
#| label: py-var-use-before-def
principal = 327.68
interest = principle * rate
#> Traceback (most recent call last):
#> File "<pyshell#4>", line 1, in <module>
#> interest = principle * rate
#> NameError: name 'principle' is not defined. Did you mean: 'principal'?
```
:::
변수명은 대소문자를 구분한다.
그래서, `LaTeX`은 {{< latex >}}, `latex`와 같지 않다.
\index{대소문자 구별, 변수명}
\index{의미론적 오류}
\index{오류!의미론}
이 지점에서 여러분이 범하기 쉬운 의미론적 오류는 연산자 우선순위일 것이다.
예를 들어 $frac{1}{2\pi}$를 계산하기 위해서 다음과 같이 프로그램을 작성하게 되면 ...
:::{.panel-tabset}
### R
```{webr-r}
#| label: r-var-pi
1.0 / 2.0 * pi
```
### 파이썬
```{pyodide-python}
#| label: py-var-pi
import math
result = 1.0 / 2.0 * math.pi
print(result)
```
:::
나눗셈이 먼저 일어나서 의도한 것과 같지 않다.
R으로 하여금 여러분이 작성한 의도를 알게 할 수는 없다.
그래서 이런 경우 오류 메시지는 없지만, 잘못된 답을 얻게 된다.