Tiếp theo bài Hằng và Biến, Chúng ta sẽ tìm hiểu khái niệm về hàm(Function) trước khi tìm hiểu tới khái niệm về Class và Struct. Hàm chính là một trong những điểm mạnh và linh hoạt trong Swift nó cũng tựa tựa với Closure. Tương tự như các ngôn ngữ lập trình khác như C/C++/Objective-C, hàm trong Swift có cú pháp khá đơn giản và cách gọi để dùng cũng dễ dàng. Trong bài này chúng ta sẽ tập trung tìm hiểu một trong những kiến thức cơ bản về hàm. Bây giờ chúng ta sẽ tạo một playground mới và bắt đầu với những ví dụ về hàm như bên dưới.

Ví dụ về hàm

Hàm là một khối block và trong khối block đó sẽ thực hiện một xử lý logic nào đó hay một công việc nào đó. Một hàm(Function) sẽ không có quá 1 khối block { } và chúng ta có thể dùng hàm bất cứ khi nào cần. Chúng ta bắt đầu khám phá một ví dụ về hàm trong Swift:

func printHelloWorld() {  
  print(“Hello World!”)
}

Bắt đầu một hàm là từ khóa func và theo sau là tên của hàm(printHelloWorld) tiếp đó là phần thân của hàm, nó được bao bọc với 2 dấu {}, Vậy để sử dụng hàm chúng ta gọi như sau:

printHelloWorld() 
// Hello World

Tham số

Hãy làm cho ví dụ trên thêm phức tạp hơn tý bằng cách thêm cáctham số  để xác định thêm các chức năng của hàm. Trong hàm này chúng ta sẽ nhập thêm một giá trị và sử dụng nó trong phần thân hàm. Chúng ta sẽ định nghĩa tên hàm printMessage(message:) và một tham sốmessage kiểu String.

Ví dụ:

func printMessage(message: String) {
   print(message)
}

Thay vì in cứng một chuỗi String bằng hàm printHelloWorld() thì bây giờ chúng ta sẽ in ra một chuỗi thông qua tham số message , cách này rất là linh hoạt và hữu dụng cho nhiều trường hợp khác nhau.

Ví dụ:

printMessage(message: “Hello World!”)
// Hello World!

Lưu ý: Có 2 khái niệm thường được dùng với hàm và thường gây ra nhầm lần cho các lập trình viên mới, đó là tham số(parameter) và đối số(arguments).Đối với Swift có sự khác biệt:

  • Tham số(parameters) là giá trị nào đó được mô tả khi khai báo hàm.
  • Đối số(arguments) của một hàm là giá trị được truyền vào hàm khi hàm đó được gọi.

Ví dụ:

printMessage(message: “Hello World!”)

Ta sẽ hiểu như sau, hàm printMessage có tham số là message với kiểu String và có đối số là “Hello World”. Đơn giản đúng không các bạn. Trong Swift, một hàm có thể có nhiều tham số và có thể định nghĩa nhiều hàm giống tên nhưng số lượng tham số phải khác nhau, như ví dụ sau:

func printMessage(message: String, times: Int) {  
    for i in 0..<times {
       print(“\(i) \(message)”)         
     }
 }
 
func printMessage(message: String) {
  print(message)
}

Hơn thế nửa hàm còn cho phép khi mô tả các tham số thì chúng ta được gán giá trị mặc định cho chúng. Chúng ta đến với ví dụ sau:

Để bắt đầu ví dụ bạn nên import thư viên: UIKit và định nghĩa hàm như sau:

import UIKit
func printDate(date: Date, format: String) {     
  let dateFormatter = DateFormatter()    
  dateFormatter.dateFormat = format
  print(dateFormatter.string(from: date))
}

Với hàm trên có tham số kiểu ngày date và format cho kiểu ngày đó. Bây giờ chúng ta thử gọi hàm đó mà không cần đối số format, thì Xcode sẽ báo lỗi như sau:

printDate(date: Date())  // error: Thiếu đối số format truyền vào

Vậy chúng ta có thể khắc phục điều đó như thế này:

func printDate(date: Date, format: String = “YY/MM/dd”) {    
  let dateFormatter = DateFormatter()   
  dateFormatter.dateFormat = format
  print(dateFormatter.string(from: date))
}

Định nghĩa một giá trị mặt định cho tham số format, sau đó gọi lại hàm và hết nhận thông báo lỗi:

printDate(date: Date())

Ngoài ra còn có thể gọi như thế này:

printDate(date: Date(), format: “dd/MM/YY”)

Lưu ý: Apple khuyến khích chúng ta nên đặt các tham số có giá trị mặc định ở phần cuối hàm, chắc chắn đó là một ý tưởng tốt và chung cho các lập trình viên trong trình bày code thêm chuyên nghiệp và đẹp hơn.

Kiểu trả về

Một hàm thì có thể có giá trị trả về hoặc không giá trị trả về, Vậy để mô tả một hàm có trị trả về ta dùng cú pháp -> sau danh sách các tham số của hàm.

Ví dụ:

func printDate(date: Date, format: String = “YY/MM/dd”) -> String {     
  let dateFormatter = DateFormatter()     
  dateFormatter.dateFormat = format
  return dateFormatter.string(from: date)
}

Ta thấy cú pháp -> và kiểu trả về là String, và trong hàm có từ khóa return để trả về kết quả, bây giờ thử gọi hàm đó và xem kết quả như thế nào.

let formattedDate = printDate(date: Date(), format: “dd/MM/YY”)
print(formattedDate)

Còn đối với hàm không có trị trả về thì chúng ta sẽ không cần dùng cú pháp -> và cũng không cần kiểu dữ liệu trả về khi định nghĩa một hàm, như ví dụ printHelloWorld() ở trên.

Một kiểu trả về khá tuyệt vời của hàm trong Swift đó là trả về kiểu Tuples hay gọi là kiểu dữ liệu trả về nhiều giá trị. Bạn có thể xem ví dụ bên dưới để hiểu rõ hơn.

Ví dụ:

func timeComponentsForDate(_ date: Date) -> (hour: Int, minute: Int, second: Int) {    
  let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date)    let hour = dateComponents.hour
  let minute = dateComponents.minute
  let second = dateComponents.second
  return (hour ?? 0, minute ?? 0, second ?? 0)
}

Swift cho phép chúng ta có thể viết gọn hơn như sau:

func timeComponentsForDate(_ date: Date) -> (Int, Int, Int) {     
  let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date)    
  let hour = dateComponents.hour
  let minute = dateComponents.minute
  let second = dateComponents.second
  return (hour ?? 0, minute ?? 0, second ?? 0)
}

Khuyến khích chúng ta nên có thêm các tên tham số để về sau dễ dàng đọc và hiểu code hơn.

Và cuối cùng, hàm cũng có thể trả về giá tri kiểu Optional như sau:

Ví dụ:

func timeComponentsForDate(_ date: Date) -> (hour: Int, minute: Int, second: Int)? {    
  let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date)   
  guard let hour = dateComponents.hour else {
    return nil
  }
  guard let minute = dateComponents.minute else {
    return nil
  }
  guard let second = dateComponents.second else {
    return nil
  }
    return (hour, minute, second)
}

Hàm là một tham số

Trong Swift, hàm cũng có thể hiểu như một kiểu dữ liệu vậy, nên nó có thể làm tham số hay kiểu trả về của một hàm nào đó. Ví dụ như (String) -> ()

Lưu ý: Cú pháp () được hiểu là kiểu Void, đó là cách viết tắt.

Ví dụ về hàm là một tham số:

func printMessage(_ message: String) {   
  print(message)
}

func printMessage(_ message: String, with function: (String) -> ()) {   
  function(message)
}
let myMessage = “Hello, world!”printMessage(myMessage, with: printMessage)
//Hello, world!

Ví dụ về  hàm là một giá trị trả về:

func compute(_ addition: Bool) -> (Int, Int) -> Int {    
  func add(_ a: Int, _ b: Int) -> Int { 
         return a + b  
  }    
  func subtract(_ a: Int, _ b: Int) -> Int {
        return a – b
  }
  if addition {
        return add     
  } else {
        return subtract
  }
}
let computeFunction = compute(true)
let result = computeFunction(1, 2)
print(result)

Với kiểu trả về là  (Int, Int) -> Int  đó cũng chính là một hàm, tuy mới lần đầu nhìn thì hơi rắc rối nhưng dần về sau sẽ quen và dễ hiểu thôi.

Trong bài viết này chúng ta đã tìm hiểu thêm một kiến thức cơ bản mới trong Swift. Quan trọng là các bạn phải hiểu được cú pháp của nó và thực hành nhiều để về sau khi tìm hiểu và học các bài viết tiếp theo sẽ dề dàng hơn. Chúng ta sẽ áp dụng bài này vào bài viết Class và Struct vì hàm thì được dùng nhiều trong Class và Struct.

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!