Các hàm functional programming trong Scala : filter và partition
1. Giới thiệu
Để góp phần tiếp nối chuỗi bài về functional programming, bài viết xin trình bày về 2 hàm tiện ích là filter và partition trong Scala. Đây là 2 hàm khá hữu ích và dễ sử dụng. Trong đó filter là hàm được dùng phổ biến hơn, tần suất tương đương với hàm map; hàm partition ít được sử dụng hơn.
2. Hàm filter
2.1. Định nghĩa
Filter là hàm được định nghĩa trong trail TraversableLike, khai báo dựa theo hàm filterImpl của TraversableLike
private def filterImpl(p: A => Boolean, isFlipped: Boolean): Repr = { val b = newBuilder for (x <- this) if (p(x) != isFlipped) b += x b.result } def filter(p: A => Boolean): Repr = filterImpl(p, isFlipped = false)
Giả sử tập hợp s chứa các phần tử có kiểu A, ta gọi hàm s.filter(p(A)).
Trong đó p(A) là hàm nhận vào 1 phần tử A, trả về kết quả kiểu Boolean (true hoặc false) dựa theo thuộc tính của A. Kết quả của s.filter(p(A)) là một tập con của tập hợp s, chứa các phần tử x thỏa mãn p(x) = true
* Đối xứng với hàm filter, ta có hàm filterNot, cũng được khai báo dựa theo hàm filterImpl
def filterNot(p: A => Boolean): Repr = filterImpl(p, isFlipped = true)
Ngược lại với filter, hàm s.filterNot(p(A)) trả về tập các phần tử x trong tập hợp s với điều kiện p(x) = false
2.2. Tính chất
– Kết quả trả về của filter là tập con của tập đầu vào, có số lượng phần tử bằng hoặc nhỏ hơn số lượng phần tử ban đầu.
– Thứ tự trước sau của các phần tử trong kết quả không thay đổi so với trong tập hợp ban đầu
2.3. Sử dụng
Nếu như hàm map được dùng như bộ chuyển đổi 1-1 cho các phần tử trong tập hợp thì hàm filter có thể hiểu là bộ lọc để lấy ra các phần tử trong tập hợp thỏa mãn yêu cầu nào nó.
val numbers = List(1, 2, 3, 5, 6, 8, 10) // numbers: List[Int] = List(1, 2, 3, 5, 6, 8, 10) val even = numbers.filter(_%2 == 0) // even: List[Int] = List(2, 6, 8, 10) val odd = numbers.filterNot(_%2 == 0) // odd: List[Int] = List(1, 3, 5)
Hàm filter có thể được sử dụng đơn lẻ hoặc kết hợp với các hàm khác như map, find để xử lý các phép toán phức tạp
val evenDoubled = numbers.filter(_%2 == 0).map(_*2) evenDoubled: List[Int] = List(4, 12, 16, 20)
3. Hàm partition
3.1. Định nghĩa
def partition(p: A => Boolean): (Repr, Repr) = { val l, r = newBuilder for (x <- this) (if (p(x)) l else r) += x (l.result, r.result) }
Cho tập hợp s gồm các phần tử có kiểu A, ta gọi hàm s.partition(p(A))
Hàm p(A) nhận vào 1 phần tử kiểu A, trả về giá trị Boolean (true hoặc false)
Kết quả của s.partition(p(A)) là cặp gồm 2 tập hợp : tập hợp đầu tiên chứa các phần tử x thỏa mãn p(x) = true, tập hợp thứ 2 chứa các phần tử y mà p(y) == false
Theo cách hiểu tương đối
s.partition(p(A)) = (s.filter(p(A)), s.filterNot(p(A)))
3.2. Tính chất
– Thứ tự trước sau của các phần tử trong 2 tập hợp của kết quả được giữ nguyên như trong tập hợp ban đầu
3.3. Sử dụng
Hàm partition được sử dụng khi muốn dùng 1 hàm điều kiện để tách tập hợp thành 2 phần : tập con thỏa mãn và tập con không thỏa mãn
val evenAndOdd = numbers.partition(_%2 == 0) evenAndOdd: (List[Int], List[Int]) = (List(2, 6, 8, 10),List(1, 3, 5))
4. Kết luận
Filter và partition là 2 hàm có cấu trúc rõ ràng, cách sử dụng dễ dàng linh hoạt. Chúng là 2 trong số những hàm cơ bản, chứa đựng sức mạnh của thành phần functional programming trong Scala. Khi sử dụng chúng, ta có thể rút ngắn được những vòng lặp kiểm tra điều kiện, từ đó giúp mã chương trình được trong sáng hơn.
Tài liệu tham khảo
http://www.scala-lang.org/api/2.10.2/index.html#scala.collection.TraversableLike