Một số phương pháp anti if theo lập trình hàm
swift
62
White

VietHQ viết ngày 20/12/2018

Bạn có biết rằng trên thế giới có một cộng đồng anti if,else. Trên đường đi tìm đường cách mệnh, coi như cũng tạo chút thử thách cho bản thân, mình đã tìm ra một số phương pháp tương đối hữu ích.

Filter

Đây là phương thức trong bộ tam filter, map, reduce ra đời kèm ngay ở phiên bản đầu của swift. Param truyền vào filter có dạng tổng quát như sau:

(T) -> Bool // hàm truyền vào 1 giá trị và return bool

Như vậy ta có thể truyền vào tên hàm, thay vì định nghĩa nó luôn.

Giả sử bài toán ta đang giải quyết là tìm số nguyên dương trong mảng, thay vì viết như:

arr.filter { (value) -> Bool in
    return value > 0
}

Ta có thể áp dụng công thức tổng quát như trên để viết gọn và rõ nghĩa hơn

// (Int) -> Bool
func positive(x: Int) -> Bool {
    return x > 0
}

arr.filter(positive)

Nếu có nhiều hơn một điều kiện, ta có thể tổng hợp lại các hàm làm một. Giả sử ta muốn tìm số vừa lớn hơn 0 vừa nhỏ hơn 100, ta viết thêm điều kiện

func lessThan100(x: Int) -> Bool {
    return x < 100
}

Sử dụng phương pháp composite, ta sẽ có 1 hàm dạng như: condition = lessThan100 & positive

public class func allPass<A>(_ array: [(A) -> Bool], value: A) -> Bool {
    let predicate = array.map({ V.bind(value: value, to: $0) }) // [() -> Bool]
    return checkAll(predicate)
}

public class func allPass<A>(_ array: [(A) -> Bool]) -> (A) -> Bool {
    return { v in
        let predicate = array.map({ V.bind(value: v, to: $0) }) // [() -> Bool]
        return checkAll(predicate)
    }
}

Mình tạo 2 phương thức đều có tên allPass, phương thức đầu trả về Bool, còn phương thức thứ hai có dạng curry function (hàm nhận vào 1 param và trả ra 1 hàm khác). Cả 2 hàm chỉ khác nhau về cách thức tạo nhưng cùng chung 1 mục đích sử dụng.

Với 2 phương thức nêu trên ta có thể composite 2 điều kiện positive và lessThan100 theo cách như sau

allPass([positive,lessThan100]) // (Int) -> Bool

Và hàm filter sẽ được viết ngắn gọn như sau:

func doFilterAll(_ value: Int) -> Bool {
    return V.allPass([positive,lessThan100], value: value)
}
arr.filter(doFilterAll)

Ta có thể làm tương tự để có phương thức Or để tìm ra giá trị thỏa mãn 1 điều khiện trong tất cả các điều kiện cung cấp.

Bạn thấy đó, với functional ta dễ dàng tách các đoạn kiểm tra điều kiện ra thành hàm riêng, dễ dàng tổng hợp chúng, đồng thời dễ test và tái sử dụng.

Để hiểu rõ hơn về hàm bindcheckAll, mình có đưa nó vào trong code ví dụ ngay cuối bài.

Refinement type

Thế nào là refinement type

refinement type = value + predicates

Như định nghĩa trên, refinement type sẽ có 2 thành phần:

+ Giá trị
+ Điều kiện

Hai thành phần này có mối liên hệ chặt chẽ, còn liên kết thế nào thì có thể xem đoạn code ngay sau đây.

Positive<Double>.of(100.0)?.value // 100
Positive<Int>.of(-1)?.value // nil
Both<Positive<Float>,LessThan100<Float>>.of(99)?.value // 99
Both<Positive<Float>,LessThan100<Float>>.of(101)?.value // nil

Như bạn thấy đã thấy, 100 thỏa mãn điều kiện lớn hơn 0, 99 thỏa mãn điều kiện nhỏ hơn 100 nên ta có thể sử dụng, trong trường hợp không thỏa mãn (-1,101) giá trị nil được trả về.

Thậm chí ta chưa dùng tí if nào :D

Đoạn code định nghĩa refinement type như sau

public protocol Refinement {
    associatedtype RefinedType
    static func pass(_ value: RefinedType) -> Bool
}

public struct Refined<A,R: Refinement> where A == R.RefinedType {
    public let value: A
    public init?(_ value: A) {
        guard R.pass(value) else { return nil }
        self.value = value
    }
}

public extension Refinement {
    static func of(_ value: RefinedType) -> Refined<RefinedType,Self>? {
        return Refined(value)
    }
}

Code ví dụ source

Mở rộng

Ngoài những cách trên thì còn rất nhiều cách để giảm thiểu if else:

  • Sử dụng đa hình
  • Tách hàm
  • switch case :v
  • Sử dụng hàm cond (như trong Lisp)
  • Blah blah

Tham khảo

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

VietHQ

16 bài viết.
7 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
7 1
Giới thiệu Callback là kĩ thuật được ưa chuộng trong lập trình hiện nay. Ngặt nỗi, nếu sử dụng không khéo rất dễ xảy ra callback hell. Dưới con mắ...
VietHQ viết 12 tháng trước
7 1
White
5 1
Thời gian đầu làm việc với objc mình khá băn khoăn trong việc sử dụng các thuộc tính trong property như strong, weak, copy, assign. Nhân lúc rảnh r...
VietHQ viết hơn 4 năm trước
5 1
White
5 0
1. Giới thiệu Từ hồi mới bắt đầu làm IOS, thằng nào cũng hỏi mình có biết sử dụng AFNetworking không? Khổ nỗi lúc đó, mình mới chuyển từ làm game ...
VietHQ viết hơn 2 năm trước
5 0
Bài viết liên quan
White
13 4
(Link) (Link) (Link) Ở 2 phần tut trước, mình đã hướng dẫn khá chi tiết cách viết một ứng dụng camera có tích hợp chức năng nhận diện khuôn mặ...
HoangPH viết hơn 4 năm trước
13 4
White
2 0
Có nhiều cách viết blog công nghệ hơn là làm bánh hay làm tình. Những ngày này Hà Nội mưa liên miên, được cái mát giời, mình lại tức cảnh sinh tìn...
VietHQ viết 2 năm trước
2 0
Male avatar
1 2
(Ảnh) Xem xét lại bản thân + Nếu bạn không quá ham mê vọc vạch và chỉ muốn được trải nghiệm macOS, bạn có thể mượn bạn bè để rờ mó... mình đùa th...
trungtran.nsn viết 6 tháng trước
1 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
16 bài viết.
7 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!