ฟังก์ชั่นการบรรทุกเกินพิกัด? ใช่หรือไม่ [ปิด]


16

ฉันกำลังพัฒนาภาษาที่คอมไพล์และพิมพ์อย่างยิ่งและฉันขอทบทวนแนวคิดว่าจะรวมฟังก์ชั่นการโอเวอร์โหลดเป็นคุณลักษณะภาษาหรือไม่ ฉันรู้ว่าฉันลำเอียงเล็กน้อยมาจากC[++|#]ภูมิหลังเป็นส่วนใหญ่

อะไรคือข้อโต้แย้งที่น่าเชื่อถือที่สุดสำหรับและต่อต้านรวมถึงฟังก์ชั่นการโอเวอร์โหลดในภาษา?


แก้ไข:ไม่มีใครที่มีความเห็นตรงข้ามหรือไม่?

เบอร์ทรานด์เมเยอร์ (ผู้สร้างหอไอเฟลย้อนกลับไปในปี 1985/1986) เรียกวิธีการดังกล่าวว่า: (ที่มา)

กลไกโต๊ะเครื่องแป้งที่ไม่นำอะไรมาสู่พลังความหมายของภาษา OO แต่การอ่านง่ายขึ้นและทำให้งานของทุกคนยุ่งยาก

ตอนนี้สิ่งเหล่านี้เป็นภาพรวมที่กว้างใหญ่ไพศาล แต่เขาเป็นคนฉลาดดังนั้นฉันคิดว่ามันปลอดภัยที่จะบอกว่าเขาสามารถสำรองได้หากเขาต้องการ ในความเป็นจริงเขาเกือบจะมีแบรดเอบรัมส์ (หนึ่งในผู้พัฒนา CLSv1) เชื่อว่า. NET ไม่ควรสนับสนุนวิธีการโอเวอร์โหลด (แหล่งที่มา)นั่นคือบางสิ่งที่ทรงพลัง ทุกคนสามารถเข้าใจความคิดของเขาและมุมมองของเขายังคงเป็นธรรมเมื่อ 25 ปีต่อมาได้หรือไม่?

คำตอบ:


24

การโอเวอร์โหลดฟังก์ชันมีความสำคัญอย่างยิ่งสำหรับ C ++ - โค้ดเทมเพลตสไตล์ ถ้าฉันต้องใช้ชื่อฟังก์ชั่นที่แตกต่างกันสำหรับประเภทที่แตกต่างกันฉันไม่สามารถเขียนรหัสทั่วไปได้ นั่นจะกำจัดส่วนที่มีขนาดใหญ่และใช้งานอย่างหนักของไลบรารี C ++ และฟังก์ชันการทำงานของ C ++ ส่วนใหญ่

มันมักจะปรากฏในชื่อฟังก์ชั่นสมาชิก A.foo()สามารถเรียกใช้ฟังก์ชั่นที่แตกต่างอย่างสิ้นเชิงจากB.foo()แต่ฟังก์ชั่นทั้งสองชื่อfooแต่ฟังก์ชั่นทั้งมีการตั้งชื่อมันมีอยู่ในโอเปอเรเตอร์เช่นเดียวกับ+สิ่งต่าง ๆ เมื่อนำไปใช้กับจำนวนเต็มและตัวเลขทศนิยมและมักใช้เป็นตัวดำเนินการเรียงสตริง ดูเหมือนว่าแปลกที่จะไม่อนุญาตให้ทำหน้าที่ปกติเช่นกัน

ช่วยให้การใช้งาน "มัลติวิธี" แบบ Common Lisp ซึ่งฟังก์ชั่นที่แน่นอนที่เรียกว่าขึ้นอยู่กับสองชนิดข้อมูล หากคุณไม่ได้ตั้งโปรแกรมไว้ใน Common Lisp Object System ให้ลองใช้ก่อนที่จะเรียกสิ่งนี้ว่าไร้ประโยชน์ มันสำคัญสำหรับสตรีม C ++

I / O ที่ไม่มีฟังก์ชั่นโอเวอร์โหลด (หรือฟังก์ชั่นแปรผันซึ่งแย่กว่า) จะต้องใช้ฟังก์ชั่นต่าง ๆ จำนวนมากไม่ว่าจะพิมพ์ค่าประเภทต่าง ๆ หรือแปลงค่าประเภทต่าง ๆ ให้เป็นประเภททั่วไป (เช่นสตริง)

หากไม่มีการใช้งานมากไปหากฉันเปลี่ยนประเภทของตัวแปรหรือค่าบางอย่างฉันต้องเปลี่ยนทุกฟังก์ชั่นที่ใช้งาน มันทำให้รหัส refactor ยากขึ้น

มันทำให้การใช้ API ง่ายขึ้นเมื่อผู้ใช้ไม่จำเป็นต้องจำแบบแผนการตั้งชื่อประเภทที่ใช้อยู่และผู้ใช้สามารถจำชื่อฟังก์ชันมาตรฐานได้

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

โดยรวมแล้วมันทำให้ภาษาใช้งานได้มากขึ้น


1
+1 คะแนนที่ดีมากทั้งหมด และเชื่อใจฉันฉันไม่คิดว่าวิธีการที่หลากหลายไร้ประโยชน์ ... ฉันสาปแช่งแป้นพิมพ์ที่ฉันพิมพ์ทุกครั้งที่ฉันถูกบังคับให้ใช้รูปแบบผู้เข้าชม
หมายเหตุถึงตัวเอง - คิดชื่อ

ผู้ชายคนนี้ไม่ได้อธิบายฟังก์ชั่นการเอาชนะและไม่ได้บรรทุกเกินพิกัด?
dragosb

8

ฉันแนะนำอย่างน้อยต้องระวังการเรียนประเภทใน Haskell คลาสประเภทถูกสร้างขึ้นเพื่อเป็นแนวทางที่มีระเบียบวินัยในการใช้งานการบรรทุกเกินพิกัด แต่พบการใช้งานอื่น ๆ และทำให้ Haskell ได้ทำสิ่งนั้น

ตัวอย่างเช่นต่อไปนี้เป็นตัวอย่างของการโหลดมากเกินไป (ไม่ Haskell ที่ถูกต้องทีเดียว):

(==) :: Int -> Int -> Bool
x == y = ...
x /= y = not (x == y)

(==) :: Char -> Char -> Bool
x == y = ...
x /= y = not (x == y)

และนี่คือตัวอย่างเดียวกันของการโอเวอร์โหลดกับคลาสชนิด:

class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool

    x /= y  =  not (x == y)

instance Eq Int where
    x == y  = ...

instance Eq Char where
    x == y  = ...

ข้อเสียของที่นี่คือที่ที่คุณต้องมากับชื่อขี้ขลาดสำหรับ typeclasses ทั้งหมดของคุณ (เช่นเดียวกับใน Haskell คุณมีนามธรรมค่อนข้างMonad, Functor, Applicativeเช่นเดียวกับการที่เรียบง่ายและเป็นที่รู้จักมากขึ้นEq, NumและOrd )

ข้อดีคือเมื่อคุณคุ้นเคยกับ typeclass แล้วคุณจะรู้วิธีใช้ประเภทใดในชั้นเรียนนั้น นอกจากนี้ยังง่ายต่อการป้องกันฟังก์ชั่นจากประเภทที่ไม่ใช้คลาสที่ต้องการเช่นใน:

group :: (Eq a) => [a] -> [[a]]
group = groupBy (==)

แก้ไข:ใน Haskell หากคุณต้องการ==โอเปอเรเตอร์ที่ยอมรับสองประเภทที่แตกต่างกันคุณสามารถใช้คลาสประเภทพารามิเตอร์หลายตัว:

class Eq a b where
    (==) :: a -> b -> Bool
    (/=) :: a -> b -> Bool

    x /= y  =  not (x == y)

instance Eq Int Int where
    x == y  = ...

instance Eq Char Char where
    x == y  = ...

instance Eq Int Float where
    x == y  = ...

แน่นอนว่านี่อาจเป็นความคิดที่ไม่ดีเนื่องจากช่วยให้คุณสามารถเปรียบเทียบแอปเปิ้ลและส้ม อย่างไรก็ตามคุณอาจต้องการพิจารณาสิ่งนี้+เนื่องจากการเพิ่ม a Word8ลงไปIntจริงๆเป็นสิ่งที่สมเหตุสมผลที่จะทำในบางบริบท


+1, ฉันพยายามเข้าใจแนวคิดนี้มาตั้งแต่ครั้งแรกที่อ่านและสิ่งนี้ช่วยได้ กระบวนทัศน์นี้ทำให้เป็นไปไม่ได้ที่จะกำหนดตัวอย่าง(==) :: Int -> Float -> Boolใด ๆ หรือไม่? (ไม่ว่าจะเป็นความคิดที่ดีหรือไม่ก็ตาม)
หมายเหตุถึงตัวเอง - คิดชื่อ

หากคุณอนุญาตให้ใช้คลาสประเภทพารามิเตอร์หลายตัว (ซึ่ง Haskell สนับสนุนเป็นส่วนขยาย) คุณสามารถทำได้ ฉันอัพเดตคำตอบด้วยตัวอย่าง
Joey Adams

อืมน่าสนใจ ดังนั้นโดยพื้นฐานแล้วclass Eq a ...แปลเป็นหลอกครอบครัว C interface Eq<A> {bool operator==(A x, A y);}และแทนที่จะใช้รหัส templated เพื่อเปรียบเทียบวัตถุโดยพลการคุณใช้ 'อินเทอร์เฟซ' นี้ นั่นถูกต้องใช่ไหม?
หมายเหตุถึงตัวเอง - คิดชื่อ

ขวา. คุณอาจต้องการดูอินเตอร์เฟสอย่างรวดเร็วใน Go กล่าวคือคุณไม่จำเป็นต้องประกาศประเภทเป็นการใช้งานอินเทอร์เฟซคุณเพียงแค่ใช้วิธีการทั้งหมดสำหรับส่วนต่อประสานนั้น
Joey Adams

1
@ Notetoself-thinkofaname: เกี่ยวกับผู้ให้บริการเพิ่มเติม - ใช่และไม่ใช่ อนุญาตให้==อยู่ในชื่อพื้นที่อื่น แต่ไม่อนุญาตให้แทนที่ โปรดทราบว่ามีเนมสเปซเดียว ( Prelude) รวมอยู่ตามค่าเริ่มต้น แต่คุณสามารถป้องกันการโหลดโดยใช้ส่วนขยายหรือโดยการนำเข้าอย่างชัดเจน ( import Prelude ()จะนำเข้าอะไรจากPreludeและimport qualified Prelude as Pจะไม่แทรกสัญลักษณ์ลงในเนมสเปซปัจจุบัน)
Maciej Piechotka

4

อนุญาตให้ฟังก์ชั่นโอเวอร์โหลดคุณไม่สามารถทำสิ่งต่อไปนี้ด้วยพารามิเตอร์ที่เป็นตัวเลือก (หรือถ้าคุณทำได้

ตัวอย่างเล็กน้อยถือว่าไม่มี base.ToString() วิธีการ

string ToString(int i) {}
string ToString(double d) {}
string ToString(DateTime d) {}
...

สำหรับภาษาที่พิมพ์อย่างหนักใช่ สำหรับภาษาที่พิมพ์น้อย คุณสามารถทำข้างต้นด้วยฟังก์ชั่นเดียวเท่านั้นในภาษาที่พิมพ์อ่อน
spex

2

ฉันต้องการพารามิเตอร์เริ่มต้นตลอดฟังก์ชั่นมากไป ฟังก์ชั่นที่โอเวอร์โหลดมักจะเรียกรุ่น "เริ่มต้น" พร้อมพารามิเตอร์เริ่มต้น ทำไมต้องเขียน

int indexOf(char ch)
{
  return self.indexOf(ch, 0);
}

int indexOf(char ch, int fromIndex)
{
  // Do whatever
}

เมื่อฉันสามารถทำ:

int indexOf(char ch, int fromIndex=0)
{
  // Do whatever
}

ที่กล่าวว่าฉันรู้ว่าบางครั้งฟังก์ชั่นโอเวอร์โหลดทำสิ่งที่แตกต่างมากกว่าแค่เรียกตัวแปรอื่นที่มีพารามิเตอร์เริ่มต้น ... แต่ในกรณีเช่นนี้มันไม่ใช่ความคิดที่แย่ (อันที่จริงมันอาจจะดีความคิด ) ชื่ออื่น

(นอกจากนี้อาร์กิวเมนต์คำหลักสไตล์งูหลามทำงานได้ดีกับพารามิเตอร์เริ่มต้นจริงๆ)


เอาล่ะเรามาลองอีกครั้งและผมจะพยายามที่จะทำให้ความรู้สึกในครั้งนี้ ... สิ่งที่เกี่ยวกับArray Slice(int start, int length) {...}เกินไปกับArray Slice(int start) {return this.Slice(start, this.Count - start);}? ไม่สามารถเข้ารหัสได้โดยใช้พารามิเตอร์เริ่มต้น คุณคิดว่าพวกเขาควรจะได้รับชื่อที่แตกต่างกันอย่างไร ถ้าเป็นเช่นนั้นคุณจะตั้งชื่อพวกเขาอย่างไร?
หมายเหตุถึงตนเอง - คิดชื่อ

ไม่ได้ระบุว่ามีการใช้งานมากเกินไปในการโหลดมากเกินไป
MetalMikester

@MetalMikester: คุณรู้สึกว่าฉันคิดถึงสิ่งที่ใช้ไป?
mipadi

@mipadi ไม่ชอบสิ่งที่indexOf(char ch)+ indexOf(Date dt)ในรายการ ฉันชอบค่าเริ่มต้นด้วย แต่ก็ไม่สามารถใช้การพิมพ์แบบคงที่ได้
ทำเครื่องหมาย

2

คุณเพิ่งอธิบาย Java หรือ C #

ทำไมคุณต้องคิดค้นล้อใหม่?

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

function getThisFirstWay(int type)
{ ... }
function getThisSecondWay(int type, double limit)
{ ... }
function getThisThirdWay(int type, String match)
{ ... }

7
มีเหตุผลที่ประเภทการส่งคืนไม่ได้เป็นส่วนหนึ่งของวิธีการที่เป็นลายเซ็นหรืออย่างน้อยก็ไม่ใช่ส่วนที่สามารถใช้สำหรับการแก้ปัญหาการโอเวอร์โหลด - ในภาษาใด ๆ ที่ฉันทราบ เมื่อคุณเรียกใช้ฟังก์ชันเป็นโพรซีเดอร์โดยไม่ต้องกำหนดผลลัพธ์ให้กับตัวแปรหรือคุณสมบัติคอมไพเลอร์ควรคิดว่าเวอร์ชันใดที่จะเรียกใช้หากอาร์กิวเมนต์อื่นทั้งหมดเหมือนกัน?
Mason Wheeler

@ Mason: คุณสามารถตรวจพบ heuristically ชนิด return ที่คาดหวังจากสิ่งที่คาดว่าจะคืน แต่ฉันไม่คาดว่าจะทำ
Josh K

1
อืม ... ฮิวริสติกของคุณรู้ได้อย่างไรว่าจะได้รับคืนอย่างไรเมื่อคุณไม่คาดหวังว่าจะได้รับผลตอบแทนใด ๆ
Mason Wheeler

1
แก้ปัญหาชนิดที่คาดหวังเป็นจริงในสถานที่ ... EnumX.Flag1 | Flag2 | Flag3คุณสามารถทำสิ่งที่ชอบ ฉันจะไม่นำสิ่งนี้ไปใช้ voidถ้าฉันได้และพิมพ์กลับไม่ได้ใช้ผมมองหาชนิดของการกลับมาของ
หมายเหตุถึงตัวเอง - คิดชื่อ

1
@ Mason: เป็นคำถามที่ดี แต่ในกรณีนั้นฉันจะหาฟังก์ชั่นโมฆะ (ดังที่กล่าวไว้) นอกจากนี้ในทางทฤษฎีคุณสามารถเลือกใด ๆ ของพวกเขาเพราะพวกเขาทั้งหมดจะดำเนินการฟังก์ชันเดียวกันก็กลับข้อมูลในรูปแบบที่แตกต่างกัน
Josh K

2

Grrr .. สิทธิ์ไม่เพียงพอที่จะแสดงความคิดเห็น ..

@Mason Wheeler: ระวัง Ada แล้วซึ่งทำ overload กับชนิด return ด้วยภาษาของฉันเฟลิกซ์ก็ทำมันในบริบทบางอย่างโดยเฉพาะเมื่อฟังก์ชั่นส่งกลับฟังก์ชั่นอื่นและมีการโทรเช่น:

f a b  // application is left assoc: (f a) b

ชนิดของ b สามารถใช้สำหรับการแก้ปัญหาการโอเวอร์โหลด นอกจากนี้ C ++ โอเวอร์โหลดกับชนิดส่งคืนในบางสถานการณ์:

int (*f)(int) = g; // choses g based on type, not just signature

ในความเป็นจริงมีอัลกอริทึมสำหรับการบรรทุกเกินพิกัดในประเภทส่งคืนโดยใช้การอนุมานประเภท ที่จริงแล้วมันไม่ยากที่จะทำกับเครื่องจักรปัญหาคือมนุษย์พบว่ามันยาก (ฉันคิดว่าเค้าร่างจะได้รับใน Dragon Book อัลกอริทึมที่เรียกว่าอัลกอริทึมดูถ้าฉันจำได้อย่างถูกต้อง)


2

ใช้กรณีกับการใช้ฟังก์ชั่นการโหลดมากเกินไป: 25 วิธีที่มีชื่อเหมือนกันว่าทำสิ่งเดียวกัน แต่มีชุดอาร์กิวเมนต์ที่แตกต่างกันโดยสิ้นเชิงในรูปแบบที่หลากหลาย

ใช้ตัวพิมพ์ใหญ่กับการไม่ใช้ฟังก์ชันโอเวอร์โหลด: 5 วิธีที่มีชื่อคล้ายกันกับชุดประเภทที่คล้ายกันมากในรูปแบบเดียวกัน

ในตอนท้ายของวันฉันไม่ได้รอคอยที่จะอ่านเอกสารสำหรับ API ที่ผลิตในกรณีใดกรณีหนึ่ง

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


1

ฉันเลือกที่จะให้เรียนรู้การโอเวอร์โหลดธรรมดาและประเภทเรียนหลายประเภทในภาษาของฉันเฟลิกซ์

ฉันพิจารณา (เปิด) จำเป็นมากเกินไปโดยเฉพาะอย่างยิ่งในภาษาซึ่งเป็นประเภทตัวเลขจำนวนมาก (เฟลิกซ์มีประเภทตัวเลขของ C ทั้งหมด) อย่างไรก็ตามแตกต่างจาก C ++ ซึ่งละเมิดการโอเวอร์โหลดโดยการสร้างเทมเพลตขึ้นอยู่กับมันความแตกต่างของเฟลิกซ์เป็นพารามิเตอร์: คุณต้องมีการโหลดมากเกินไปสำหรับเทมเพลตใน C ++ เนื่องจากเทมเพลตใน C ++ ออกแบบมาไม่ดี

คลาสประเภทมีให้ใน Felix เช่นกัน สำหรับผู้ที่รู้ภาษา C ++ แต่อย่าห้อมล้อม Haskell ให้เพิกเฉยต่อคนที่อธิบายว่าเป็นการใช้งานมากเกินไป มันไม่ใช่จากระยะไกลเช่นการบรรทุกเกินพิกัด แต่ก็เหมือนกับเทมเพลตเชี่ยวชาญ: คุณประกาศแม่แบบที่คุณไม่ได้ใช้งานจากนั้นให้การใช้งานสำหรับกรณีเฉพาะตามที่คุณต้องการ parametrically polymorphic การใช้งานคือการสร้างอินสแตนซ์เฉพาะกิจ แต่มันไม่ได้ตั้งใจที่จะไม่มีข้อ จำกัด : มันต้องใช้ซีแมนทิกส์ที่ตั้งใจไว้

ใน Haskell (และ C ++) คุณไม่สามารถระบุความหมายได้ ใน C ++ แนวคิด "Concepts" นั้นเป็นความพยายามในการประมาณความหมาย ในเฟลิกซ์คุณสามารถประมาณความตั้งใจด้วยสัจพจน์การลดบทแทรกและทฤษฎีบท

หลักและเพียงประโยชน์จาก (เปิด) การบรรทุกเกินพิกัดในภาษา principled ดีเช่นเฟลิกซ์ก็คือว่ามันทำให้ง่ายต่อการจำชื่อฟังก์ชั่นห้องสมุดทั้งสำหรับนักเขียนโปรแกรมและรหัสวิจารณ์

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

ใน C ++ นี่ก็เป็นปัญหาเพราะมันมีอัลกอริทึมการจับคู่เลอะเทอะและยังสนับสนุนการแปลงประเภทอัตโนมัติ: ใน Felix I "แก้ไข" ปัญหานี้โดยต้องการการจับคู่ที่แน่นอนและไม่มีการแปลงประเภทอัตโนมัติ

ดังนั้นคุณมีทางเลือกฉันคิดว่า: การอนุมานมากไปหรือการอนุมานประเภท การอนุมานน่ารัก แต่ก็ยากที่จะนำไปใช้ในวิธีที่วินิจฉัยความขัดแย้งได้อย่างถูกต้อง ยกตัวอย่างเช่น Ocaml จะบอกคุณว่ามันตรวจจับความขัดแย้งที่ไหน แต่ไม่ได้อนุมานถึงประเภทที่คาดหวังไว้

การโหลดมากเกินไปนั้นไม่ดีกว่าแม้ว่าคุณจะมีคอมไพเลอร์คุณภาพที่พยายามบอกผู้สมัครทุกคนมันก็ยากที่จะอ่านว่าผู้สมัครนั้นเป็น polymorphic และยิ่งแย่กว่านั้นถ้ามันเป็นแม่แบบ C ++


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

ใช่ไซต์ทั้งหมดอยู่ในระหว่างการก่อสร้าง (อีกครั้ง) ขออภัย
Yttrill

0

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


0

หากคุณต้องการให้ผู้ใช้มีความคุ้นเคยกับภาษาตระกูล C แล้วใช่คุณควรเพราะผู้ใช้ของคุณจะคาดหวัง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.