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 một 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á trì hoãn, để đạt được mức tối ưu hoá và hiệu quả đáng kể.

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à việc không có tác dụng phụ. Điều này có nghĩa là các hàm mà bạn viết không dựa vào hoặc cập nhật dữ liệu nằm bên 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 đề để 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ể được á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 từng số bằng cách sử dụ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 gặp phải khi quen với mô hình lập trình theo quy trình là việc sử dụng đúng 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 có nhiều chức năng hơn bằng cách sử dụng map() và bộ lọc. Earth Engine sử dụng thực thi trì hoãn, tức là quá trình đánh giá một biểu thức sẽ bị trì hoãn cho đến khi giá trị thực của biểu thức đó thực sự cần thiết. Trong một số trường hợp, kiểu mô hình thực thi này sẽ đánh giá cả lựa chọn thay thế đúng và sai của câu lệnh ee.Algorithms.If(). Điều này có thể dẫn đến việc tính toán và sử dụng bộ nhớ nhiều hơn, tuỳ thuộc vào các biểu thức và tài nguyên cần thiết để thực thi chúng.

Giả sử bạn muốn giải một biến thể của ví dụ trên, trong đó nhiệm vụ là chỉ tính bình phương của các số lẻ. Dưới đây là cách tiếp cận theo 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 phù hợp 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 tốt nhất 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 từng tập hợp con. Điều này cho phép hệ thống song song hoá hoạt động. 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());

Số lần 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 được dùng cho lần lặp tiếp theo. Earth Engine cung cấp phương thức iterate() cho những tác 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 ra dãy số Fibonacci. Trong đó, mỗi số trong dãy 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 được truyền vào 2 giá trị, giá trị hiện tại trong quá trình 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 một 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 Earth Engine API.