Các khái niệm về lập trình hàm

Giới thiệu về lập trình hàm

Earth Engine sử dụng hệ thống xử lý song song để thực hiện tính toán trên một số lượng lớn máy. Để cho phép xử lý như vậy, Earth Engine tận dụng các kỹ thuật tiêu chuẩn thường được các ngôn ngữ chức năng sử dụng, chẳng hạn như tính minh bạch tham chiếu và đánh giá lười, để tối ưu hoá đáng kể và tăng hiệu quả.

Khái niệm chính giúp phân biệt lập trình hàm với lập trình thủ tục là không có tác dụng phụ. Điều này có nghĩa là các hàm bạn viết không dựa vào hoặc cập nhật dữ liệu nằm ngoài hàm. Như bạn sẽ thấy trong các ví dụ bên dưới, bạn có thể tái cấu trúc vấn đề để có thể giải quyết bằng các hàm không có tác dụng phụ – phù hợp hơn nhiều để thực thi song song.

Vòng lặp for

Bạn không nên sử dụng vòng lặp for trong Earth Engine. Bạn có thể đạt được kết quả tương tự bằng cách sử dụng thao tác map(), trong đó bạn chỉ định một hàm có thể áp dụng độc lập cho từng phần tử. Điều này cho phép hệ thống phân phối quá trình xử lý đến các máy khác nhau.

Ví dụ dưới đây minh hoạ cách bạn lấy một danh sách số và tạo một danh sách khác có bình phương của mỗi số bằng map():

Trình soạn thảo mã (JavaScript)

// This generates a list of numbers from 1 to 10.
var myList = ee.List.sequence(1, 10);

// The map() operation takes a function that works on each element independently
// and returns a value. You define a function that can be applied to the input.
var computeSquares = function(number) {
  // We define the operation using the EE API.
  return ee.Number(number).pow(2);
};

// Apply your function to each item in the list by using the map() function.
var squares = myList.map(computeSquares);
print(squares);  // [1, 4, 9, 16, 25, 36, 49, 64, 81]

Điều kiện If/Else

Một vấn đề phổ biến khác mà người dùng mới quen với mô hình lập trình thủ tục gặp phải là việc sử dụng đúng các toán tử điều kiện if/else trong Earth Engine. Mặc dù API này cung cấp thuật toán ee.Algorithms.If(), nhưng bạn không nên sử dụng thuật toán này mà nên dùng phương pháp hiệu quả hơn bằng cách sử dụng map() và bộ lọc. Earth Engine sử dụng quá trình thực thi trì hoãn, nghĩa là việc đánh giá một biểu thức sẽ bị trì hoãn cho đến khi giá trị thực tế của biểu thức đó thực sự cần thiết. Trong một số trường hợp, loại mô hình thực thi này sẽ đánh giá cả hai phương án đúng và sai của câu lệnh ee.Algorithms.If(). Điều này có thể dẫn đến việc sử dụng thêm tài nguyên tính toán và mức sử dụng bộ nhớ, tuỳ thuộc vào biểu thức và tài nguyên cần thiết để thực thi các biểu thức đó.

Giả sử bạn muốn giải một biến thể của ví dụ trên, trong đó nhiệm vụ là tính bình phương của chỉ các số lẻ. Dưới đây là một phương pháp chức năng để giải quyết vấn đề này mà không cần điều kiện if/else:

Trình soạn thảo mã (JavaScript)

// The following function determines if a number is even or odd.  The mod(2)
// function returns 0 if the number is even and 1 if it is odd (the remainder
// after dividing by 2).  The input is multiplied by this remainder so even
// numbers get set to 0 and odd numbers are left unchanged.
var getOddNumbers = function(number) {
  number = ee.Number(number);   // Cast the input to a Number so we can use mod.
  var remainder = number.mod(2);
  return number.multiply(remainder);
};

var newList = myList.map(getOddNumbers);

// Remove the 0 values.
var oddNumbers = newList.removeAll([0]);

var squares = oddNumbers.map(computeSquares);
print(squares);  // [1, 9, 25, 49, 81]

Mô hình này đặc biệt hữu ích khi làm việc với các tập hợp. Nếu bạn muốn áp dụng một thuật toán khác cho tập hợp dựa trên một số điều kiện, thì cách ưu tiên là trước tiên hãy lọc tập hợp dựa trên điều kiện, sau đó map() một hàm khác cho mỗi tập hợp con. Điều này cho phép hệ thống thực hiện thao tác song song. Ví dụ:

Trình soạn thảo mã (JavaScript)

// Import Landsat 8 TOA collection and filter to 2018 images.
var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')
  .filterDate('2018-01-01', '2019-01-01');

// Divide the collection into 2 subsets and apply a different algorithm on them.
var subset1 = collection.filter(ee.Filter.lt('SUN_ELEVATION', 40));
var subset2 = collection.filter(ee.Filter.gte('SUN_ELEVATION', 40));

// Multiply all images in subset1 collection by 2;
// do nothing to subset2 collection.
var processed1 = subset1.map(function(image) {
  return image.multiply(2);
});
var processed2 = subset2;

// Merge the collections to get a single collection.
var final = processed1.merge(processed2);
print('Original collection size', collection.size());
print('Processed collection size', final.size());

Lặp lại tích luỹ

Bạn có thể cần thực hiện thao tác tuần tự, trong đó kết quả của mỗi lần lặp lại được dùng cho lần lặp lại tiếp theo. Earth Engine cung cấp phương thức iterate() cho những nhiệm vụ như vậy. Hãy nhớ rằng iterate() được thực thi theo cách tuần tự và do đó sẽ chậm đối với các thao tác lớn. Chỉ sử dụng khi bạn không thể dùng map() và bộ lọc để đạt được kết quả mong muốn.

Một ví dụ minh hoạ hay về iterate() là việc tạo dãy số Fibonacci. Ở đây, mỗi số trong chuỗi là tổng của 2 số trước đó. Hàm iterate() nhận 2 đối số, một hàm (thuật toán) và một giá trị bắt đầu. Bản thân hàm này được truyền vào 2 giá trị, giá trị hiện tại trong lần lặp và kết quả của lần lặp trước đó. Ví dụ sau đây minh hoạ cách triển khai dãy Fibonacci trong Earth Engine.

Trình soạn thảo mã (JavaScript)

var algorithm = function(current, previous) {
  previous = ee.List(previous);
  var n1 = ee.Number(previous.get(-1));
  var n2 = ee.Number(previous.get(-2));
  return previous.add(n1.add(n2));
};

// Compute 10 iterations.
var numIteration = ee.List.repeat(1, 10);
var start = [0, 1];
var sequence = numIteration.iterate(algorithm, start);
print(sequence);  // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Giờ đây, khi đã hiểu rõ các khái niệm về JavaScript, bạn có thể xem Hướng dẫn về API để biết thông tin giới thiệu về chức năng không gian địa lý của API Earth Engine.