แนวคิดการเขียนโปรแกรมเชิงฟังก์ชัน

ข้อมูลเบื้องต้นเกี่ยวกับการเขียนโปรแกรมเชิงฟังก์ชัน

Earth Engine ใช้ระบบการประมวลผลแบบขนานเพื่อทำการคำนวณในเครื่องจำนวนมาก เพื่อให้สามารถประมวลผลดังกล่าวได้ Earth Engine จึงใช้ประโยชน์จากเทคนิคมาตรฐานที่ใช้กันโดยทั่วไปในภาษาเชิงฟังก์ชัน เช่น ความโปร่งใสในการอ้างอิงและการประเมินแบบ Lazy เพื่อเพิ่มประสิทธิภาพและประสิทธิผลอย่างมาก

แนวคิดหลักที่ทำให้การเขียนโปรแกรมเชิงฟังก์ชันแตกต่างจากการเขียนโปรแกรมเชิงกระบวนการคือการไม่มีผลข้างเคียง ซึ่งหมายความว่าฟังก์ชันที่คุณเขียนไม่ได้อาศัยหรืออัปเดตข้อมูลที่อยู่นอกฟังก์ชัน ดังที่คุณจะเห็นในตัวอย่างด้านล่าง คุณสามารถปรับโครงสร้างปัญหาเพื่อให้แก้ไขได้โดยใช้ฟังก์ชันที่ไม่มีผลข้างเคียง ซึ่งเหมาะสำหรับการดำเนินการแบบขนานมากกว่า

สำหรับวนซ้ำ

เราไม่แนะนำให้ใช้ for-loop ใน Earth Engine คุณจะได้ผลลัพธ์เดียวกันโดยใช้การดำเนินการ 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

ปัญหาที่พบบ่อยอีกประการหนึ่งที่ผู้ใช้ใหม่ซึ่งคุ้นเคยกับกระบวนทัศน์การเขียนโปรแกรมเชิงกระบวนการต้องเผชิญคือการใช้โอเปอเรเตอร์แบบมีเงื่อนไข if/else อย่างเหมาะสมใน Earth Engine แม้ว่า API จะมีอัลกอริทึม ee.Algorithms.If() แต่เราไม่แนะนำให้ใช้ และแนะนำให้ใช้วิธีการที่มีประสิทธิภาพมากกว่าโดยใช้ map() และตัวกรอง Earth Engine ใช้ การดำเนินการที่เลื่อนออกไป ซึ่งหมายความว่าการประเมินนิพจน์จะเลื่อนออกไปจนกว่าจะจำเป็นต้องใช้ค่าที่รับรู้จริง ในบางกรณี รูปแบบการดำเนินการประเภทนี้จะประเมินทั้งทางเลือกที่เป็นจริงและเท็จของคำสั่ง ee.Algorithms.If() ซึ่งอาจทำให้มีการคำนวณและการใช้งานหน่วยความจำเพิ่มเติม ทั้งนี้ขึ้นอยู่กับนิพจน์และทรัพยากรที่จำเป็นในการดำเนินการ

สมมติว่าคุณต้องการแก้โจทย์ที่ต่างไปจากตัวอย่างข้างต้น โดยงานคือการคำนวณกำลังสองของเลขคี่เท่านั้น แนวทางเชิงฟังก์ชันในการแก้ปัญหานี้โดยไม่มีเงื่อนไข 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