函數式程式設計概念

函式程式設計簡介

Earth Engine 會使用平行處理系統,在大量機器上執行運算。為啟用這類處理作業,Earth Engine 會運用函數語言常用的標準技術,例如參照透明度和延遲評估,大幅提升最佳化和效率。

函式程式設計與程序式程式設計的主要概念差異在於沒有副作用。也就是說,您編寫的函式不會依賴或更新函式外部的資料。如下列範例所示,您可以重新架構問題,以便使用沒有副作用的函式解決問題,這類函式更適合平行執行。

For 迴圈

我們不建議在 Earth Engine 中使用 for 迴圈。您也可以使用 map() 作業,指定可獨立套用至每個元素的函式,達到相同結果。這樣一來,系統就能將處理作業分配到不同機器。

以下範例說明如何使用 map() 取得數字清單,並建立另一個清單,其中包含每個數字的平方:

程式碼編輯器 (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]

If/Else 條件

對於習慣程序式程式設計範例的新手來說,在 Earth Engine 中正確使用 if/else 條件運算子是另一個常見問題。雖然 API 提供 ee.Algorithms.If() 演算法,但我們強烈建議使用 map() 和篩選器,採取功能更完善的做法。Earth Engine 使用 延後執行,也就是說,系統會延後評估運算式,直到實際需要運算式的值為止。在某些情況下,這類執行模型會評估 ee.Algorithms.If() 陳述式的 true 和 false 替代方案。視運算式和執行運算式所需的資源而定,這可能會導致額外的運算和記憶體用量。

假設您想解決上述範例的變體,也就是只計算奇數的平方。以下說明如何以函式方法解決這個問題,而不使用 if/else 條件:

程式碼編輯器 (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]

這個範例特別適用於處理集合。如果想根據某些條件,對集合套用不同的演算法,建議先根據條件篩選集合,然後對每個子集 map() 不同的函式。這樣一來,系統就能平行處理作業。例如:

程式碼編輯器 (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());

累計疊代

您可能需要執行循序作業,其中每次疊代的結果會用於後續疊代。Earth Engine 提供 iterate() 方法來執行這類工作。請注意,iterate() 是依序執行的,因此大型作業會很慢。只有在無法使用 map() 和篩選器取得所需輸出內容時,才使用這項功能。

iterate() 的良好示範是建立斐波那契數序列。在這裡,數列中的每個數字都是前 2 個數字的總和。iterate() 函式會接受 2 個引數:函式 (演算法) 和起始值。函式本身會傳遞 2 個值:疊代中的目前值,以及前一次疊代的結果。以下範例說明如何在 Earth Engine 中實作費波那契數列。

程式碼編輯器 (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]

現在您已充分瞭解 JavaScript 概念,可以參閱 API 教學課程,瞭解 Earth Engine API 的地理空間功能。