โครงสร้างควบคุม (Control Structures)
โครงสร้างควบคุมใช้เพื่อควบคุมการไหลของประโยคคำสั่งที่ถูกสั่งให้ทำงานโดยตัวแปลของ R โดยรวมประโยคคำสั่งเงื่อนไขและลูป R คำนวณตามคำสั่งประโยคคำสั่งเหล่านั้นตามลำดับโดยคำนวนหาค่า ประโยคคำสั่งสามารถจัดให้อยู่แยกกันโดยใช้เซมิโคลอนคั่นหรือไม่ก็ขึ้นบรรทัดใหม่ เซมิโคลอนนำมาใช้เพื่อสร้างประโยคคำสั่งที่แยกจากกัน เป็นการช่วยตัวแปลเพื่อแยกแยะประโยคคำสั่ง และทำให้สามารถเข้าใจประโยคคำสั่งได้ดีขึ้น และอ่านง่ายขึ้นกว่าเดิมสำหรับโปรแกรมเมอร์ ทั้งเซมิโคลอน และ บรรทัดใหม่ที่จัดวางไว้ตอนท้ายประโยคคำสั่ง ถ้าประโยคคำสั่งไม่สมบูรณ์ ตัวแปลจะปล่อยไว้ไม่สนใจว่าถ้า ส่วนการโปรแกรมนี้อยู่ในภาวะปฏิสัมพันธ์หรือไม่ เครื่องหมายเตรียมพร้อมจะเปลี่ยนจากหัวลูกศรทางขวา(>) ไปเป็นเครื่องหมายบวก (+).
ตัวอย่างต่อไปแสดงส่วนการโปรแกรม (session) ตอบสนองเมื่ออยู่ในภาวะปฏิสัมพันธ์
> x <- 0; x + 7
[ 1] 7
เมื่อตัวแปลตรวจสอบประโยคคำสั่งหนึ่งสมบูรณ์แล้ว ตัวแปลคำนวณตามคำสั่งแล้วคือกลับให้ค่าหนึ่ง ผลลัพธ์ที่ให้คืนกลับมาพิจารณาเป็นค่าของประโยคคำสั่ง ที่ค่านี้สามารถกำหนดให้กับสัญลักษณ์ ประโยคคำสั่งภายใต้โครงสร้างควบคุมจัดกลุ่มด้วยวงเล็บปีกกาเปิด ({) และวงเล็บปีกกาปิด(}). ประโยคคำสั่งที่อยู่ในวงเล็บจะเรียกว่าบล็อก บล็อกจะถูกอ่านหลักจากปิดวงเล็บปีกกา ประโยคคำสั่งหนึ่งจะถูกอ่านเข้ามา เมื่อบรรทัดใหม่ถูกสร้างขึ้นที่ท้ายประโยคคำสั่งที่สมบูรณ์. เหมือนในนัทเชลล์หนึ่ง (a nutshell),ประโยคคำสั่งที่อ้างถึงประโยคคำสั่งเดี่ยวหรือบล็อกหนึ่ง ต่อไปเป็นตัวอย่างหนึ่งของประโยคคำสั่งหนึ่งที่ปิดล้อมด้วยวงเล็บ
> { x <- 0
> + x + 4 }
[1] 4
R ใช้โครงสร้างควบคุม if, if/else, while, repeat, และ for เพื่อให้เกิดผลสำหรับประโยคคำสั่งเงื่อนไขและทำงานซ้ำ ไวยากรณ์ต่อไปนี้จะแสดงให้เราถึงการเขียนโครงสร้างควบคุมในการประยุกต์ใช้
if -- if (เงื่อนไข) นิพจน์; ตัวอย่างเช่น:
if(x>y)
{ print (X is greater than Y) }
if/else – if (เงื่อนไข) นิพจน์1 else นิพจน์2; ตัวอย่างเช่น:
if (2==1) {print(Yes)}
else print(No)
while – while(เงื่อนไข) นิพจน์; ตัวอย่างเช่น:
x <- 1
while(x < 4)
{ x <- x + 2 print(x)}
repeat – repeat นิพจน์; ตัวอย่างเช่น:
x <- 1
repeat { x <- x + 1
print(z) break() }
for – for (variables in sequence) statement; ตัวอย่างเช่น
for (value in seq(0,1,by=0.3))
{ result(value,"\n"); }
นิพจน์เหล่านี้ในโครงสร้างควบคุมข้างบนนี้ปกติแล้วอ้างถึงนิพจน์แบบคอมปาวด์ (compound expressions) ใน while, repeat และ สำหรับการก่อเกิดลูป เราสารมารถใช้ break ที่จะหยุดทำงานของลูป และต่อไปเพื่อไปต่อการทำงานซ้ำต่อไป(การซ้ำกระบวนการ). การเกิดโครงสร้างทำงานซ้ำ “if”, “while”, “repeat”, “for”, “break”, และ “next” ถูกเก็บไว้ภายในของฟังก์ชัน.
การสั่งปฏิบัติงานตามเงื่อนไขและการทำงานซ้ำ (Conditional and repeat Execution)
ใน R ใช้ การสร้างเงื่อนไขและทำงานซ้ำเพื่อหาค่าเดี่ยวและหลายค่า โดยใช้ประโยคคำสั่งของ “if” และ “if/else” เพื่อหาค่าเงื่อนไขหนึ่งเพื่อทำให้เกิดผลลัพธ์ที่ถูกต้อง
การสั่งปฏิบัติตามเงื่อนไข Conditional Execution
การให้ปฏิบัติตามเงื่อนไขเป็นการสร้างตามเงื่อนไขที่ใช้ไวยากรณ์ต่อไปนี้
if (นิพจน์1) นิพจน์2 else นิพจน์3 ไวยากรณ์ “if/else” แสดงให้เห็นถึง“นิพจน์ion1” ต้องถูกคำนวณหาค่าได้ผลเป็นค่าหนึ่ง ตัวกระทำAND(&&) และ OR(||) บ่อยครั้งที่ใช้ในประโยคคำสั่ง“if” สำหรับสัญลักษณ์ตัวกระทำเดี่ยวAND(&) และOR(|) ในอีกทางหนึ่งประยุกต์ใช้กับเว็คเตอร์บนฐานขององค์ประกอบเวคเตอร์ ตัวกระทำ AND (&&) และOR (||) อย่างไรก็ตามเมื่อประยุกต์ใช้กับเวคเตอร์ที่มีความยาวเดียว และเฉพาะคำนวณหาค่านิพจน์ที่2 ถ้าจำเป็น R ใช้เว็คเตอร์เป็นฐาน โครงสร้าง if/else ที่เกิดขึ้นเรียกว่าฟังก์ชัน ifelse อย่างหนึ่ง โดยใช้ไวยากรณ์ดังต่อไปนี้
ifelse(condition, x, y)
ฟังก์ชัน “ifelse” คืนกลับเป็นเวคเตอร์หนึ่งด้วยความยาวของอาร์กูเมนต์ที่ยาวที่สุด ประกอบด้วยองค์ประกอบ elements a[i] และ b[i], ที่ซึ่ง [i] เป็นจริง, else สั่งปฏิบัติ b[i].
ปฏิบัติคำสั่งทำงานซ้ำ (Repetitive Execution)
กาปฏิบัติคำสั่งทำงานซ้ำเกี่ยวข้องกับ โครงสร้าง for loops, repeat และ while .
“for” loop ใช้ไวยากรณ์ดังนี้
for (variable_name in expression1) expression2
ไวยากรณ์ “for” loop จะมีตัวแปรลูป เป็นเหมือน “variable_name” และนิพจน์เวคเตอร์เป็นเหมือน “ expression1”. นิพจน์เว็คเตอร์มักจะมีลำดับที่เห็นเป็น1:20(1 row; 20 vectors). นิพจน์ที่เป็นกลุ่มคือ “expression2”, ซึ่งถูกตีซ้ำผ่านตามค่าของผลลัพธ์เว็คเตอร์ใน “expression1”. ถ้าเรามีตัวอย่างหนึ่งและเรามี“vec” เป็นเหมือนเว็คเตอร์หนึ่งของ class indicators. ถ้าเราประสงค์ที่จะสร้างการพล็อตการแยก x และ y
ในคลาส เราคงจะใช้ฟังก์ชัน coplot() นี่เป็นการสร้างอะเรย์หนึ่งของการพล็อตที่สอดคล้องกับแต่ละระดับ ของแฟคเตอร์นั้น เราคงทำเช่นนี้ด้วยเช่นกันโดยการใช้การพล็อตทั้งหมดในการแสดงผลครั้งเดียว
ตัวอย่างต่อไปนี้แสดงวิธีการใช้การพล็อตทั้งหมดต่อการแสดงผลครั้งเดียวได้อย่างไร
> xc <- split(x, vec)
> yc <- split(y, vec)
for (i in 1:length(yc))
{ plot(xc[[i]], yc[[i]])
abline(lsfit(xc[[i]], yc[[i]])) }
ฟังก์ชัน split() สร้างรายการของเว็คเตอร์ ที่ทำให้ได้มาซึ่งการแยกเว็คเตอร์ที่โตกว่าบนฐานของคลาสกำหนดโดยเว็คเตอร์ ฟังก์ชันนี้มักจะใช้ใน boxplots.
สำคัญ: ในโค๊ด R “for” loops ไม่ได้ใช้เป็นปกติทั่วไปในภาษาแบบคอมไพล์ โค๊ดเมื่อมองที่ออฟเจ็ค ทั้งหมด ปรากฏว่าเร็วกว่าดีกว่า
อีกชนิดหนึ่งของลูปที่ใช้ใน R รวมทั้งประโยคคำสั่ง repeat และ while ประโยคคำสั่ง break นำมาใช้กับลูปเพื่อหยุด/จบ การกระทำ เป็นหนทางเดียวที่เราสามารถหยุดการทำงานซ้ำของลูป ประโยคคำสั่งต่อไปที่่นำมาใช้จบไซเคิลเฉพาะ และเคลื่อนไปยัง“next”
หมายเหตุ: ประโยคคำสั่งควบคุมปกติแล้วใช้กับฟังก์ชัน
การ่่ทำงานซ้ำ Iteration
ในตอนนี้เราจะเรียนรู้เกี่ยวกับการทำงานซ้ำพอสังเขป This is an important topic for programmers because it is used to control the flow of elements within a program, as well as analyzing data. In data analysis iterative statements powers procedures for calculating complex datasets and advanced mathematical calculations relating to statistics. Loops and Vectorization R allows you to create loops and calculate vectors using iteration in R. Loops are used to repeat an operation for a specific number of times. In the following example, you will see how a simple for loop is created. > for (i in 1:n) > { cat("Name #", i, "\n") } [1] Name # 1 Name # 2 Name # 3 Name # 4 In the above example of the for loop, the counter is set and the loop runs for “n” number of times. The result shows that the loop iterated 4 times. The loop actually created one line of output for each value of n=(1, 2, 3) that is passed through the index “I”. The final element in the in the string concatenation is “\n” – a new line break is implemented. This example shows how iteration works with a for loop. You will learn more about “for” loops later on in this chapter. The example can be replaced by applying the function with the three elements of the vector I, , 2, 3. 4. You can do this because the paste() function is vectored. This method can be used to substitute looping in many situations. In the above example, you would replace “cat("Name #" , i, "\n")” with “paste(Country #” , 1:4)”. This is vectored approach. The while loop is similar to the “for” loop and uses iteration until a condition is either true or false. You may also say that the while loop condition is true until it is not. Pay close attention when writing the conditions, because the loop can iterate indefinitely. An indefinite loop is only necessary under special circumstances. Here is an example of a while loop: i <- 5 while (i > 0) { print(i <- i - 1) } [1] 4 [1] 3 [1] 2 [1] 1 [1] 0 // output In the above example of the while loop, the counter is set (i<-5) and is decreased by 1 until it gets to 0. There are more complex ways to solve problems with while loops, like searching for the solution to a “finding-the-number” fame. This is called the “brute force” approach (no shortcuts). You will learn more about writing while loops later on in this chapter. There are other loops that are sequentially exhaustive and can become very intensive to calculate when they iterate through arrays with values. When this happens, you may need to optimize the code and use several processors to reduce the response time of the loop. You can iteratively execute a loop or vectorize the loop. Most loops can be replaced with vectorization because it is more efficient to manage the data this way. Vectorized functions are used in R because they run faster than memory-intensive loops with built-in code. The idea is to apply a specific function instead of instructing R to apply the function separately. The following examples will show you how to write an iterative loop and a vectorized loop. // An iterative loop square i, square i + 1, square i + 2, for (i in 1:2) print(i^2) [1] 1 [1] 4 // A vectorized loop define i = { 1, 2 } (1:2)^2 [1] 1 4 In the above example, the vector is 1, 2. The square function is used to evaluate the vectors, which returns the results for the vector. Be aware that many of the functions in R accept vectors as arguments, which makes it easier to calculate. This helps to resolve potential conflicts. In the following examples, you will see how combination vectors are used. // This example shows two possible conflicts in a single combination. prod(1:2)/prod(1:2) [1] 1 // This example shows 3 possible combinations. prod(1:3)/(prod(1:2) * prod(1:1)) [1] 3 //This example shows 6 possible combinations. prod(1:4)/(prod (1:2) * prod(1:2)) [1] 6 The structure of this function is called the “explosive roommate function”. It looks at the number of potential conflicts that is possible with each “roommate” that is affecting the other. The code calculates the value using the following equation: \(\frac{n!}{k!(n-k)!}\), where \(n\) is the number of roommates and \(k = 2\). The function can be used to calculate increasing numbers. You can also plot part of the function by creating a vector of values and apply the combinations. It is done by using the sapply() function. This is the vectorized equivalent of the “for” loop. The following example shows the “explosive roommate function” written by Francis Smart. // Explosive roommates function. explosive_roommates = function(n) prod(1:n) / (prod(1:2) * prod(1:max(n - 2, 1))) The “explosive roommates function” is applied first when returning a vector of results. The sapply() function is then used to go through the vector of values x and return a vector of results y. This is demonstrated below in the following examples. Step 1: The vector of the x value is defined. x = 2:20 Step 2: The sapply() function is used to establish vectors of y values. y = sapply(x , explosive_roommates) Step 3: The function plots y against x. qplot(x, y, geom = c("line", "point")) + labs(y = "Number of potential conflicts", x = "Number of roommates") Note: You can test the above steps in R to see the results. Make sure that you declare the “explosive roommates function” before you enter them. Looping Looping is an advanced concept in R and can be challenge for those who are new to programming. R has looping structures like any other programming language that requires time and effort to master. Looping is a repeated evaluation for a block of statements. The traditional for, while, and repeat loops are used to evaluate statements, but there are other constructs that are used to control the evaluations. There is an unconventional repeat loop and a family of loops called “apply”, “sapply”, “lapply”, and “tapply”. These are used for implicit looping. Traditional loops like for and while loops are used for repetitive actions, but the Apply loop family are used in specific circumstances, such as in ragged arrays. You can also use loops within loops, for example you can use a “while” loop within a “for” loop. The for, while, and repeat loops are used in R for explicit looping. To control the loops R uses the next and break built-in constructs during an evaluation. The break statement allows you to exit the loop that is currently being executed. The next statement allows you to return to the beginning of the loop to execute the next iteration of the loop. Any statements that are below the statement in this instance will not be evaluated. After an evaluation, loops will return the value of the last statement that was evaluated. You can also assign the results from the for, while, and repeat statements to a symbol. Looping is not necessary in all operations. You can use vectors that do not require a loop. Values that are returned by loop statements are always NULL, which is not generally noticeable. Here is some background information that will help you understand how loops work in R: 1. Loops are slow in R. Although they are slow loops will accomplish the task in reasonable time unless you are working with a large dataset. If you are working with lots of data, there ways to get around it. 2. R is written in a C or some variant of C++ type language. When you execute an R code, you are actually executing C code. R uses the underlying C code to run loops with or without vectors. 3. R provides alternative functions that you can use instead of using loops. The “apply” function for example, works faster than the “for” loop because it actually has built-in for loop written in R. There are also other functions in the apply family that runs faster than loops. However, a well-constructed loop can run just as fast as apply functions. When programming in R, you may encounter a problem within a loop. If this happens refer to the following key points: 1. Reset the set values – You may have to reset the value or a vector within the loop. It is possible that you may have used a statement within the “for” loop that has a value that needs to reset. 2. Missing brackets – Sometimes you may have forgotten to put in your square brackets or curly braces within the counter or to the left or right of the statement. If/Else Statement The if/else statement is conditional and evaluates two or more statements. The arguments within the if” statement is a logical expression. It is a single block of code, where more blocks can be added after the else statement. It is used to evaluate a logical value. If the value is valid, the first statement is evaluated, and if the first statement is NOT TRUE, then the second statement is evaluated. A value is then returned. The following is the syntax for the if/else statement: > if ( statement1=TRUE) > (result1) > else > result3 Here is an example of an “if/else” statement. > x <- 3 > if(x == 5) { print(1) } > else { print(2) } [1] 2 The first “if” statement (x == 5) in the above example is evaluated to produce a value. If the value is TRUE in the first statement, the second statement (print (1)) is evaluated. If the first statement (x==5) is FALSE, the second statement (print(1)) is ignored and the third statement (print(2)) is evaluated. If the value is not logical or valid, an error is returned. If/else statements are used to prevent numeric problems, such as the logarithm of a negative number. Since if/else statements are the same as other statements you can assign values to them. In the following “if/else” examples, both statements produce the same results. if( any(y <= 1) ) y <- log(1+y) else x <- log(y) x <- if( any(y <= 1) ) log(1+y) else log(y) The else clause in both examples is optional. Instead, the statement if (any(y<=1)) y <- y[y <= 1] can be used without the else clause. When the “if“ statement is not within the block and the else is present, you must have the second statement on the same line. If not, the new line at the end of second statement completes the “if“ statement and returns a complete evaluated statement. The easiest way to resolve this is to insert open and close braces ({ }) for compound statements and by placing the else on the same line as the closing brace (}). The closing brace specifies the end of the statement. The following ‘if/else” syntax shows how you would nest compound or multiple statements: if ( statement1 ) { statement2 } else if ( statement3 ) { statement4 } else statement6 The even numbered statements in the example are evaluated and return a value. If the else clause is ignored and the odd numbered statements return FALSE, then no other statements are evaluated and a NULL value is returned. The odd numbered statements are evaluated in order until one of them returns TRUE. The even numbered statements are then evaluated. In the above example, statement4 is only evaluated if statement1 returns FALSE. R allows you to write multiple “if” statements in a conditional form. The first statement/expression is evaluated and returns a single logical value. It allows you to use double AND (&&) and OR (||) operators with the conditional “if” statements. You can use the following operators to produce “TRUE” or “FALSE”, “T” or “F”, “1” or “0” results in if statements. x == y -- This means x is equal to y, for example: if (10 == 5 + 5) print(Yes) x != y -- This means x is not equal to y, for example: if (10 ! = 5 + 5) print(No) x > y -- This means x is greater than y, for example: if (10 > 5 ) print(It is greater) x < y -- This means x is less than y, for example: if (10 < 5) print(It is not greater) x <= y -- This means x is less than or equal to y, for example: if (10 <= 5 + 5) print (It is equal) x >= y -- This means x is greater than or equal to y, for example: if (5 >= 10) print (It is less) The “else” statement on the other hand is an alternate option. Ideally, the else statement much be written on the same line as the closing brace for the previous “if” block. R provides a vector version for the “if/else” statement. It is the “if/else” function. The following syntax is used to write the if/else function. ifelse(condition, x, y) This function returns vectors of various lengths and holds the longest argument. It contains the elements x[i] if condition [i] is TRUE, else y[i]. The arguments use vectors of various lengths. The “condition” argument is used to test, x is assessed as a “TRUE” value, and y is assessed a “FALSE” value. The following example is an “ifelse” function that displays 5 vectors. x <- 1:5 ifelse(x<5 | x>5, x, 0) [1] 1 2 4 4 0 The ifelse() function takes the first condition, and then takes the second condition if the first is TRUE and the third if the condition is FALSE. The condition can be a vector in this case. In this example, the results show a vector sequence of numbers from 1 to 5. All the values displayed are less than 5 and greater than 5.