#!/usr/bin/env python
# coding: utf-8

# In[ ]:


<h1>ฟังก์ชัน (Functions)</h1>
<p><strong>Functions</strong><br>
    <pre>ฟังก์ชั่น หรือ บางครั้งเราเรียกว่า โปรแกรมย่อย มีความสำคัญมาก เมื่อเราเขียนโปรแกรมได้สักระยะ เราจะเห็นว่าโปรแกรมของเราจะมีจำนวนบรรทัดมากขึ้น ยากแก่การตรวจสอบแก้ไขข้อผิดพลาด ทางออกคือ ต้องสร้างเป็นส่วนย่อยๆ เพื่อสะดวกในการแก้ไขและหาข้อผิดพลาดได้ง่าย </pre>
<p>ฟังก์ชั่นแบ่งออกเป็น 2 ประเภท</p>
<ol>
    <li>ฟังก์ชั่นที่ผู้เขียนโปรแกรมสร้างขึ้นเอง (User Defined Functions)</li>
    <li>ฟังก์ชั่นที่สร้างจากผู้อื่นถูกเก็บไว้ในไลบรารี (Library) มีทั้งนำมาใช้ได้ฟรี (Open Source) และมีค่าใช้จ่าย (Commercial)</li>
</ol>

</p>


# <h2>การสร้างฟังก์ชั่นขึ้นมาใช้งาน (Creating Function)</h2>
# <p>
#     การสร้างฟังก์ชั่นสามารถแบ่งวิธีการสร้างและการทำงานของฟังก์ชั่นออกได้ 4 รูปแบบ คือ
# </p>
# 

# 1. การสร้างฟังก์ชั่นที่ไม่มีการส่งค่าและรับค่า<br><br>
# ตัวอย่างที่ 12.1 โปรแกรมแสดงข้อความ Hello โดยไม่มีการส่งค่าและรับค่า

# In[21]:


h = float(input("high : "))
b = float(input("lengh : " ))
def triangle():
    arear=0.5*h*b
    print (arear)
triangle()


# 2. การสร้างฟังก์ชั่นที่ไม่มีการส่งค่าแต่มีการรับค่า<br><br>
# ตัวอย่างที่ 12.2 โปรแกรมแสดงข้อความ Hello โดยมีการส่งค่าชื่อ ไต่ไม่มีการรับค่า

# In[25]:


def triangle(h,b):
    h=int(h)
    b=int(b)
    arear=0.5*h*b
    print (arear)
triangle(5,6)


# 3. การสร้างฟังก์ชั่นที่มีการส่งค่าแต่ไม่มีการรับค่า<br><br>
# ตัวอย่างที่ 12.3 โปรแกรมแสดงข้อความ ที่ส่งกลับมาจากฟังก์ชั่นด้วยคำสั่ง return

# In[26]:


h = float(input("high : "))
b = float(input("lengh : " ))
def triangle():
    arear=0.5*h*b
    return arear
print (triangle())


# 4. การสร้างฟังก์ชั่นที่มีการส่งค่ากลับและมีการรับค่า<br><br>
# ตัวอย่างที่ 12.4 โปรแกรมที่มีการส่งอาร์กิวเมนต์ให้กับพารามิเตอร์ และมีการส่งค่ากลับมายังโปรแกรมที่เรียกใช้งาน ด้วยคำสั่ง return

# In[31]:


def triangle (h,b):
    Arear =  0.5*h*b
    return Arear
print (triangle(12.5,10))


# <h2>ข้อควรจำ</h2>
# <p><strong>อาร์กิวเมนต์ (Argument)</strong><br>
#     <pre>คือ ค่าตัวแปร หรือ ค่าคงที่ ที่ใช้สำหรับส่งค่าไปยังฟังก์ชั่นที่เรียกใช้งาน เพื่อนำไปประมวลผลต่อ ค่าอาร์กิวเมนต์จะถูกแนบส่งไปพร้อมกับชื่อฟังก์ชั่นอยู่ในเครื่องหมายวงเล็บ หากมีอาร์กิวเมนต์หลายตัวจะคั่นด้วยเครื่องหมาย comma (,) </pre>
# <p><strong>พารามิเตอร์ (Parameter)</strong><br>
#     <pre>คือ ตัวแปรที่ประกาศไว้รอรับค่าอาร์กิวเมนต์ อยู่ในเครื่องหมายวงเล็บหลังชื่อฟังก์ชั่น หากมีพารามิเตอร์หลายตัวจะคั่นด้วยเครื่องหมาย comma (,) </pre>
# <!--<img src="img/argument.png",width=60,height=60>-->
# <img src="img/argument.png">

# <h3>การส่งค่าอาร์กิวเมนต์มี 2 วิธี คือ</h3><br>
# <h4>1.การส่งค่าข้อมูล (Call by Value)</h4><br>
# <pre>ค่าอาร์กิวเมนต์จะถูกทำการ copy แล้วส่งให้กับพารามิเตอร์ของฟังก์ชันที่เราเรียกใช้งาน จำนวนพารามิเตอร์ต้งเท่ากับจำนวนอาร์กิวเมนต์ที่ส่งมาให้ การเปลี่ยนแปลงค่าพารามิเตอร์ภายในฟังก์ชัน จะไม่มีผลกระทบค่าอาร์กิวเมนต์เมื่อมีการส่งค่ากลับ การส่งค่าอาร์กิวเมนต์แบบนี้ทำให้สิ้นเปลืองเนื้อที่ในหน่วยความจำ</pre>
# 
# <h4>2.การส่งค่าแบบอ้างอิง (Call by Reference)</h4><br>
# <pre>ใช้กับข้อมูลชนิดออปเจ็ค โดยส่งค่าตำแหน่งอาร์กิวเมนต์ในหน่วยความจำ ไปให้กับพารามิเตอร์ในฟังก์ชันที่เรียกใช้งาน เมื่อมีการเปลี่ยนแปลงข้อมูลของค่าพารามิเตอร์จะทำให้อาร์กิวเมนต์ที่ส่งไปเปลี่ยนตามไปด้วย การส่งอาร์กิวเมนต์แบบนี้มีข้อดีคือ ประหยัดเนื้อที่ในหน่วยความจำ เพราะค่าอาร์กิวเมนต์ไม่ต้องมีการ copy อีกชุดหนึ่งเหมือนกับวิธีการแบบแรก และลดเวลาในการประมวลผลข้อมูล</pre>
# 
# 

# ตัวอย่างที่ 12.5 การส่งค่าแบบ Call by Value

# In[ ]:


r = int(input("ป้อนค่ารัศมี : "))
h = int(input("ป้อนค่าความสูง : "))
def cylinder():
    area = 3.14 * (r * r) * h
    return area
print ("ปริมาตรทรงกระบอก = ", cylinder(), "ตารางเมตร")


# ตัวอย่างที่ 12.6 การส่งค่าอาร์กิวเมนต์ให้กับค่าพารามิเตอร์ไปยังฟังก์ชันที่เรียกใช้งาน เป็นชนิดข้อมูลลิสต์ซึ่งเป็นชนิดข้อมูลที่สามารถเปลี่ยนแปลงได้ โดยการเพิ่มข้อมูลเข้าไปในลิสต์ด้วยเมธอด append()

# In[ ]:


def msg(f_lst):
    print ("ค่่าพารามิเตอร์ที่รับมาจาก arg_lst =", f_lst)
    f_lst.append("easy to learn.")
    print ("เปลี่ยนค่าพารามิเตอร์ = ", f_lst)

arg_lst = ["Python is"]
print ("ค่าอาร์กิวเมนต์ที่ส่งให้ค่าพารามิเตอร์ =", arg_lst)
msg(arg_lst)
print ("ค่าอาร์กิวเมนต์หลังจากเปลี่ยนค่าพารามิเตอร์ =", arg_lst)


# ตัวอย่างที่ 12.7 การส่งค่าอาร์กิวเมนต์ให้กับค่าพารามิเตอร์ไปยังฟังก์ชันที่เรียกใช้งาน เป็นชนิดข้อมูลลิสต์ซึ่งเป็นชนิดข้อมูลที่สามารถเปลี่ยนแปลงได้ โดยการเพิ่มข้อมูลเข้าไปในลิสต์ด้วยการระบุตำแหน่ง

# In[ ]:


def msg(f_lst):
    print ("ค่่าพารามิเตอร์ที่รับมาจาก arg_lst =", f_lst)
    f_lst[0] = "Python is a powerful programming language."
    print ("เปลี่ยนค่าพารามิเตอร์ = ", f_lst)

arg_lst = ["Python is easy to learn."]
print ("ค่าอาร์กิวเมนต์ที่ส่งให้ค่าพารามิเตอร์ =", arg_lst)
msg(arg_lst)
print ("ค่าอาร์กิวเมนต์หลังจากเปลี่ยนค่าพารามิเตอร์ =", arg_lst)


# <h3>รูปแบบการส่งค่าอาร์กิวเมนต์ให้กับค่าพารามิเตอร์</h3>
# 
# <h4>การส่งค่าอาร์กิวเมนต์แบบ Required arguments</h4>
# <pre>เป็นการส่งอาร์กิวเมนต์ให้พารามิเตอร์ ตามชนิดข้อมูล และลำดับ โดยต้องมีจำนวนเท่ากันด้วย ดังตัวอย่างที่ 12.8</pre>
# <h4>การส่งค่าอาร์กิวเมนต์แบบ Keyword arguments</h4>
# <pre>เป็นการส่งอาร์กิวเมนต์ให้พารามิเตอร์ได้โดยไม่ต้องเรียงลำดับ แต่ต้องมีชื่อเดียวกันและต้องมีจำนวนเท่ากัน ดังตัวอย่างที่ 12.9</pre>
# <h4>การส่งค่าอาร์กิวเมนต์แบบ Default arguments</h4>
# <pre>มีค่าพารามิเตอร์บางตัวถูกกำหนดค่าไว้ล่วงหน้า ซึ่งเมื่อเรียกใช้ก็จะส่งค่าอาร์กิวเมนต์เฉพาะจำนวนที่เหลือให้พารามิเตอร์เท่านั้น ดังตัวอย่างที่ 12.10</pre>
# <h4>การส่งค่าอาร์กิวเมนต์แบบ Variable-length arguments</h4>
# <pre>เป็นการสร้างพารามิเตอร์ไว้รับค่าอาร์กิวเมนต์แบบไม่จำกัดจำนวน โดยชื่อพารามิเตอร์ต้องมีเครื่องหมาย (*)นำหน้า ดังตัวอย่างที่ 12.11</pre>

# ตัวอย่างที่ 12.8

# In[ ]:


def req_arg(numlst, str_, num):
    print ("แสดงค่าข้อมูลในลิสต์ =", numlst)
    print ("แสดงค่าข้อมูลในตัวแปร num =", num)
    print ("แสดค่าข้อมูลในตัวแปร str_ =", str_)

lst = [1,2,3]; msg = "Python"
req_arg(lst, msg, 50)


# ตัวอย่างที่ 12.9

# In[ ]:


def key_arg(lst, msg, num):
    print("แสดงค่าข้อมูลในลิสต์ =", lst)
    print("แสดงค่าข้อมูลในตัวแปร num =", num)
    print("แสดงค่าข้อมูลในตัวแปร msg =", msg)


key_arg(lst=[1, 2, 3], num=10, msg="Python is easy")


# ตัวอย่างที่ 12.10

# In[ ]:


def def_arg(r, pi=3.14):
    result = 2* pi * r
    print("เส้นรอบวงกลม =", result)
def_arg(7)


# ตัวอย่างที่ 12.11

# In[ ]:


def varleng_arg(*num):
    print("จำนวนข้อมูลในตัวแปร num :")
    for var in num:
        print(var, end=" ")
    print(" ")
    result = num[2] * 2
    print("ผลคูณพารามิเตอร์ num[2] * 2 =", result)

varleng_arg(10, 20, 30, 40)


# <h2>Lambda Expressions</h2>
# <p>Lambda Expressions คือ anonymous function ที่เป็นฟังก์ชันที่มีการทำงานขนาดเล็กอยู่ภายในที่สามารถมีได้เพียง Expression เดียวเท่านั้น เราสามารถสร้างโดยใช้คำสั่ง <code>lambda</code> เราสามารถใช้ Lambda Expressions สร้างออบเจ็คของฟังก์ชันได้ และค่า return จะเป็นค่าที่ได้จากผลลัพธ์ของ Expression ของฟังก์ชัน  มาดูตัวอย่างการใช้งาน</p>
# <pre><code >f = lambda x: x + 1
# print(f(2))
# print(f(8))
# 
# g = lambda a, b: (a + b) / 2
# print(g(3, 5))
# print(g(10, 33))
# 
# def make_incrementor(n):
#     return lambda x: x + n
# 
# f = make_incrementor(13)
# print(f(0))
# print(f(1))
# print(f(5))</code></pre>
# <p>ในตัวอย่าง เราได้สร้าง Lambda Expressions เป็นจำนวนสามฟังก์ชัน ฟังก์ชันแรกเป็นฟังก์ชันสำหรับเพิ่มตัวเลขขึ้น 1 และฟังก์ชันที่สองเป็นฟังก์ชันสำหรับหาค่าเฉลี่ยของตัวเลขสองจำนวน คุณจะสังเกตุได้ว่าฟังก์ชันแรกนั้นมี 1 อาร์กิวเมนต์และฟังก์ชันที่สองนั้นมี 2 อาร์กิวเมนต์ และฟังก์ชันสุดท้ายนั้นเป็นการ return ฟังก์ชันกลับภายในฟังก์ชันอีกที และเป็นฟังก์ชันสำหรับเพิ่มตัวเลขขึ้นจำนวน n จากอาร์กิวเมนต์ที่ใส่เข้าไป</p>
# <pre><code >3
# 9
# 4.0
# 21.5
# 13
# 14
# 18</code></pre>
# <p>นี่เป็นผลลัพธ์การทำงานของโปรแกรม</p>
# <p>นอกจากนี้ Lambda Expressions ยังมีประโยชน์เพื่อใช้งานกับ built-in function เช่น ฟังก์ชัน <code>filter()</code> และฟังก์ชัน <code>map()</code>  ในภาษา Python มันใช้เป็นอาร์กิวเมนต์ส่งเข้าไปในฟังก์ชัน เพื่อสร้าง Expression ให้กับฟังก์ชัน มาดูตัวอย่างการใช้งาน</p>
# <pre><code >numbers = [2, 15, 5, 7, 10, 3, 28, 30]
# print(list(filter(lambda x: x % 5 == 0, numbers)))
# print(list(map(lambda x: x * 2, numbers)))</code></pre>

# ตัวอย่างที่ 12.12 การสร้างฟังก์ชัน lambda หาปริมาตรทรงกระบอก

# In[ ]:


area = lambda r, h: 3.14 * (r*r) * h
print ("ปริมาตรทรงกระบอกที่ 1 = ", area(3,5))
print ("ปริมาตรทรงกระบอกที่ 2 = ", area(8,12))
print ("ปริมาตรทรงกระบอกที่ 3 = ", area(9,15))


# ตัวอย่างที่ 12.13 การสร้างฟังก์ชัน lambda ที่ไม่มีการส่งค่าอาร์กิวเมนต์

# In[ ]:


area = lambda: 3.14 * 5 * 5 * 10
print ("ปริมาตรทรงกระบอก = ", area())


# ตัวอย่างที่ 12.14 การสร้างฟังก์ชัน lambda ครั้งละหลายๆ ฟังก์ชั่น ที่อยู่ภายในชนิดข้อมูลลิสต์

# In[ ]:


area = [lambda r, h: 3.14 * (r * r) * h,
       lambda r: 3.14 * (r * r),
       lambda b, h: 0.5 * b * h,
       lambda w, h: w * h]
print ("ปริมาตรทรงกระบอก = ", area[0](5, 6), "ตารางเมตร")
print ("พื้นที่วงกลม = ", area[1](3), "ตารางเมตร")
print ("พื้นที่สามเหลี่ยม = ", area[2](5, 7), "ตารางเมตร")
print ("พื้นที่สี่เหลี่ยมผืนผ้า = ", area[3](10, 12), "ตารางเมตร")


# ตัวอย่างที่ 12.15 การสร้างตัวแปรโกลบอลและการเรียกใช้งาน โดยฟังก์ชัน cylinder

# In[ ]:


r = int(input("ป้อนค่ารัศมี : "))
h = int(input("ป้อนค่าความสูง : "))
def cylinder():
    area = 3.14 * (r * r) * h
    return area
print ("ปริมาตรทรงกระบอก = ", cylinder(), "ตารางเมตร")


# ตัวอย่างที่ 12.16 การสร้างตัวแปรโลคอลขึ้นมาใช้งานภายในฟังก์ชัน และการเรียกใช้งานตัวแปรข้ามฟังก์ชัน

# In[ ]:


def circle_area():
    global r; r = 10
    global pi; pi = 3.14
    result = pi * (r * r)
    return result
def circumference():
    result = 2 * pi * r
    return result
print ("พื้นที่วงกลม = ", circle_area(), "ตารางเมตร")
print ("เส้นรอบวงวงกลม = %.3f" %circumference(), "เมตร")


# ตัวอย่างที่ 12.17 การสร้างตัวแปรโลคอลและโกลบอลที่มีชื่อเหมือนกัน

# In[ ]:


cash = int(input("ป้อนจำนวนเงิน : "))
vat = 0.07
total = cash - (cash * vat)
def cash(cash):
    total = cash - (cash * vat)
    return total
print ("ชำระเป็นจำนวนเงิน = ", cash(5000), "บาท")
print ("ชำระเป็นจำนวนเงิน = ", total, "บาท")


# ตัวอย่างที่ 12.18 การสร้างตัวแปรโลคอลที่มีชื่อเหมือนกัน แต่ใช้เฉพาะในฟังก์ชันของใครของมัน ไม่เกี่ยวกัน

# In[ ]:


def bonus_1(m, ot):
    total =  (m + (ot * 20)) * 0.5
    salary = total + m
    return salary
def bonus_2(m, ot):
    total =  (m + (ot * 20)) * 0.2
    salary = total + m
    return salary

print ("เงินเดือนที่ได้รับสุทธิ = ", bonus_1(12000, 5), "บาท")
print ("เงินเดือนที่ได้รับสุทธิ = ", bonus_2(20000, 5), "บาท")


# <h4>สรุป</h4>
# <p>ฟังก์ชันเป็นส่วนหนึ่งที่มีความสำคัญในการพัฒนาโปรแกรม ซึ่งในเนื้อหาตอนนี้ได้แนะนำวิธีการสร้างฟังก์ชันขึ้นมาใช้งานแบบต่างๆ เช่น การสร้างฟังก์ชั่นที่ไม่มีการส่งค่า การสร้างฟังก์ชั่นที่ไม่มีการคืนค่า การสร้างฟังก์ชั่นที่มีการรรับค่าแต่ไม่มีการส่งค่ากลับ เป็นต้น นอกจากนี้ยังได้รู้จักกับวิธีการส่งค่าอาร์กิวเมนต์ให้กับค่าพารามิเตอร์ และยังได้รู้จักวิธีการสร้างค่าพารามิเตอร์ไว้รอรับค่าอาร์กิวเมนต์ รวมไปถึงสร้างฟังก์ชันที่ไม่ต้องกำหนดชื่อ ด้วย <code>lambda</code> ทำให้ลดเวลาในการเขียนคำสั่งโปรแกรม ซึ่งที่กล่าวมาทั้งหมดนี้ ไม่ต้องใช้คำสั่ง import เลย</p>

# In[ ]:




