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

Hàm partition cũng được định nghĩa trong trait TraversableLike
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

– Hàm partition luôn trả về 1 cặp gồm 2 tập hợp, tập hợp thỏa mãn p(x) == true đứng trước, tập hợp có p(x) == false đứng sau
– 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

Add a Comment

Scroll Up