สามารถนำ varargs ไปใช้ได้อย่างไร เราต้องการกลไกบางอย่างในการส่งสัญญาณการสิ้นสุดของรายการอาร์กิวเมนต์ สิ่งนี้สามารถเป็นได้
- ค่าเทอร์มิเนเตอร์พิเศษหรือ
- ความยาวของรายการ vararg ส่งผ่านเป็นพารามิเตอร์พิเศษ
กลไกทั้งสองนี้สามารถใช้ในบริบทของการแก้ปัญหาเพื่อนำ varargs มาใช้ แต่การพิมพ์ที่เหมาะสมกลายเป็นปัญหาหลัก สมมติว่าเรากำลังเผชิญกับฟังก์ชั่นsum: ...int -> int
ยกเว้นว่าฟังก์ชั่นนี้ใช้ currying (ดังนั้นเราจึงมีประเภทที่คล้ายกันมากขึ้นsum: int -> ... -> int -> int
ยกเว้นว่าเราไม่ทราบจำนวนของข้อโต้แย้ง)
ค่า Terminator: กรณีอนุญาตend
เป็น Terminator พิเศษและเป็นชนิดของT
sum
ตอนนี้เรารู้ว่านำไปใช้กับend
ผลตอบแทนที่ฟังก์ชั่นนี้sum: end -> int
และที่นำไปใช้กับ int เราได้รับอีก sum: int -> T
sum-เช่นฟังก์ชั่น: ดังนั้นT
การรวมกันของประเภทเหล่านี้: T = (end -> int) | (int -> T)
. โดยการแทนT
เราได้รับเป็นไปได้ประเภทต่างๆเช่นend -> int
, int -> end -> int
, int -> int -> end -> int
ฯลฯ แต่ส่วนใหญ่ระบบการพิมพ์ไม่รองรับประเภทดังกล่าว
ตัวพิมพ์: ความยาวอย่างชัดเจน: อาร์กิวเมนต์แรกของฟังก์ชัน vararg คือจำนวนของ varargs ดังนั้นsum 0 : int
, sum 1 : int -> int
, sum 3 : int -> int -> int -> int
ฯลฯ ซึ่งได้รับการสนับสนุนในระบบการพิมพ์บางอย่างและเป็นตัวอย่างของการพิมพ์ขึ้น ที่จริงจำนวนของการขัดแย้งจะเป็นพารามิเตอร์ชนิดและไม่ได้เป็นพารามิเตอร์ปกติ - มันจะไม่ทำให้ความรู้สึกของ arity ของการทำงานขึ้นอยู่กับค่ารันไทม์ที่s = ((sum (floor (rand 3))) 1) 2
จะเห็นได้ชัดไม่ดีพิมพ์: ประเมินนี้อย่างใดอย่างหนึ่งs = ((sum 0) 1) 2 = (0 1) 2
, หรือs = ((sum 1) 1) 2 = 1 2
s = ((sum 2) 1) 2 = 3
ในทางปฏิบัติไม่ควรใช้เทคนิคเหล่านี้เนื่องจากมีแนวโน้มที่จะเกิดข้อผิดพลาดและไม่มีประเภท (มีความหมาย) ในระบบประเภททั่วไป sum: [int] -> int
แต่เพียงแค่ผ่านรายการของค่าพารามิเตอร์เป็นหนึ่งดังนี้
ใช่มันเป็นไปได้สำหรับวัตถุที่จะปรากฏเป็นทั้งฟังก์ชั่นและค่าเช่นในระบบประเภทที่มีการข่มขู่ อนุญาตsum
เป็นSumObj
, ซึ่งมีการบีบบังคับสองแบบ:
coerce: SumObj -> int -> SumObj
อนุญาตให้sum
ใช้เป็นฟังก์ชันและ
coerce: SumObj -> int
ช่วยให้เราสามารถดึงผลลัพธ์
ในทางเทคนิคนี่คือการเปลี่ยนแปลงของกรณีค่า terminator ข้างต้นด้วยT = SumObj
และcoerce
เป็น un-wrapper สำหรับประเภท ในภาษาเชิงวัตถุจำนวนมากสิ่งนี้สามารถนำไปใช้งานได้อย่างมากกับตัวดำเนินการโอเวอร์โหลดเช่น C ++:
#include <iostream>
using namespace std;
class sum {
int value;
public:
explicit sum() : sum(0) {}
explicit sum(int x) : value(x) {}
sum operator()(int x) const { return sum(value + x); } // function call overload
operator int() const { return value; } // integer cast overload
};
int main() {
int zero = sum();
cout << "zero sum as int: " << zero << '\n';
int someSum = sum(1)(2)(4);
cout << "some sum as int: " << someSum << '\n';
}