ข้อดีและข้อเสียของการใช้คลาสเพื่อห่อหุ้มอัลกอริธึมเชิงตัวเลขคืออะไร


13

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

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

ในขณะที่วิธีการดั้งเดิมคือการมีฟังก์ชั่นหนึ่งที่เรียกใช้ฟังก์ชั่นอื่น ๆ จำนวนมากผ่านการขัดแย้งที่เกี่ยวข้องไปพร้อมกัน OOP นำเสนอวิธีการที่แตกต่างกัน เพื่อความชัดเจนโดยการห่อหุ้มอัลกอริทึมในคลาสฉันหมายถึงการสร้างคลาสที่อินพุตอัลกอริทึมถูกป้อนเข้าไปในตัวสร้างคลาสแล้ววิธีการสาธารณะเรียกว่าอัลกอริทึมจริงเรียก การใช้งาน multigrid ใน C ++ psuedocode อาจมีลักษณะเช่นนี้:

class multigrid {
    private:
        x_, b_
        [grid structure]

        restrict(...)
        interpolate(...)
        relax(...)
    public:
        multigrid(x,b) : x_(x), b_(b) { }
        run()
}

multigrid::run() {
     [call restrict, interpolate, relax, etc.]
}

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


2
มันเป็นสัญญาณที่ไม่ดีเสมอเมื่อชื่อคลาสของคุณเป็นคำคุณศัพท์แทนคำนาม
David Ketcheson

3
คลาสสามารถใช้เป็นเนมสเปซไร้สัญชาติสำหรับการจัดระเบียบฟังก์ชันเพื่อจัดการกับความซับซ้อน แต่มีวิธีอื่นในการจัดการความซับซ้อนในภาษาที่จัดให้มีคลาส (เนมสเปซใน C ++ และโมดูลใน Python เป็นสิ่งสำคัญ)
Geoff Oxberry

@GeoffOxberry ฉันไม่สามารถพูดได้ว่านี่เป็นการใช้งานที่ดีหรือไม่ดี - ซึ่งเป็นเหตุผลที่ฉันถามในตอนแรก - แต่คลาสที่ไม่เหมือนกับ namespaces หรือโมดูลยังสามารถจัดการ "สถานะชั่วคราว" เช่นลำดับชั้นของกริด ใน multigrid ที่ถูกยกเลิกเมื่ออัลกอริทึมเสร็จสิ้น
Ben

คำตอบ:


13

หลังจากใช้ซอฟต์แวร์ตัวเลขมา 15 ปีแล้วฉันสามารถระบุสิ่งต่อไปนี้ได้อย่างไม่น่าสงสัย:

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

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

  • เมื่อเทียบกับระบบซอฟต์แวร์อื่น ๆ มันอาจจะเป็นความจริงที่ว่าลำดับชั้นของชั้นเรียนมีความสำคัญน้อยกว่าพูดในส่วนติดต่อผู้ใช้แบบกราฟิก แน่นอนว่ามีสถานที่ในซอฟต์แวร์เชิงตัวเลขที่มีความโดดเด่น - Jed ให้คำตอบหนึ่งในหัวข้อนี้กับอีกคำตอบหนึ่งคือหลายวิธีที่เราสามารถใช้แทนเมทริกซ์ได้ (หรือโดยทั่วไปแล้ว PETSc ทำสิ่งนี้อย่างสม่ำเสมอด้วยฟังก์ชั่นเสมือนจริงสำหรับการดำเนินการทั้งหมดที่ใช้เมทริกซ์ (พวกเขาไม่เรียกมันว่า "ฟังก์ชั่นเสมือนจริง" แต่นั่นคือสิ่งที่มันเป็น) มีพื้นที่อื่น ๆ ในรหัสองค์ประกอบ จำกัด โดยทั่วไปที่หนึ่งใช้หลักการออกแบบนี้ของซอฟต์แวร์ OO สิ่งที่อยู่ในใจคือสูตรการสร้างพื้นที่สี่เหลี่ยมจัตุรัสและองค์ประกอบไฟไนต์หลายชนิด ซึ่งทั้งหมดจะแสดงโดยธรรมชาติเป็นหนึ่งอินเตอร์เฟส / การนำไปใช้งานจำนวนมาก คำอธิบายกฎหมายวัสดุจะตกอยู่ในกลุ่มนี้ แต่มันอาจจะเป็นความจริงที่เกี่ยวกับมันและส่วนที่เหลือของรหัสองค์ประกอบ จำกัด ไม่ได้ใช้การสืบทอดเป็นที่แพร่หลายอย่างที่คน ๆ หนึ่งอาจใช้มันใน GUI

จากจุดสามจุดเหล่านี้ควรมีความชัดเจนว่าการเขียนโปรแกรมเชิงวัตถุนั้นสามารถนำไปใช้กับรหัสตัวเลขได้อย่างแน่นอนเช่นกัน อาจเป็นเรื่องจริงที่ BLAS / LAPACK ไม่ได้ใช้กระบวนทัศน์นี้ (และอินเตอร์เฟสปกติที่ถูกเปิดเผยโดย MATLAB ไม่ได้ทำ) แต่ฉันจะลองเดาว่าซอฟต์แวร์ตัวเลขที่ประสบความสำเร็จทุก 10 ปีที่ผ่านมาคือความจริงแล้ว เชิงวัตถุ


16

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

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


8

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

ในกรณีของ OpenFOAM ชิ้นส่วนอัลกอริทึมจะถูกนำไปใช้ในแง่ของตัวดำเนินการทั่วไป (div, grad, curl, ฯลฯ ) ซึ่งโดยทั่วไปแล้วจะมีฟังก์ชั่นนามธรรมที่ใช้กับเมตริกซ์ประเภทต่าง ๆ โดยใช้รูปแบบตัวเลข รหัสนี้เป็นส่วนหนึ่งที่สร้างขึ้นจากอัลกอริทึมทั่วไปจำนวนมากที่ทำงานในชั้นเรียน สิ่งนี้ทำให้ลูกค้าสามารถเขียนสิ่งที่ชอบ:

solve(ddt(U) + div(phi, U)  == rho*g + ...);

ลำดับชั้นเช่นโมเดลการขนส่งโมเดลความปั่นป่วนรูปแบบที่แตกต่างรูปแบบการไล่ระดับสีเงื่อนไขขอบเขต ฯลฯ ถูกนำมาใช้ในแง่ของคลาส C ++ (อีกครั้งทั่วไปในปริมาณเทนเซอร์)

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

โครงสร้างลำดับชั้น ==> คลาส

ขั้นตอนการดำเนินงานแผนภูมิการไหล ==> อัลกอริทึม


5

แม้ว่านี่จะเป็นคำถามเก่า แต่ฉันคิดว่ามันคุ้มค่าที่จะพูดถึงวิธีแก้ปัญหาเฉพาะของจูเลีย สิ่งที่ภาษานี้ทำคือ "class-less OOP": โครงสร้างหลักคือประเภทเช่นวัตถุข้อมูลคอมโพสิตคล้ายกับstructs ใน C ซึ่งมีการกำหนดความสัมพันธ์ของการสืบทอด ชนิดไม่มี "ฟังก์ชันสมาชิก" แต่แต่ละฟังก์ชันมีลายเซ็นประเภทและยอมรับชนิดย่อย ตัวอย่างเช่นคุณอาจมีนามธรรมMatrixชนิดและเชื้อDenseMatrix, SparseMatrixและมีวิธีการทั่วไปที่มีความเชี่ยวชาญdo_something(a::Matrix, b::Matrix) การส่งหลายครั้งจะใช้เพื่อเลือกเวอร์ชันที่เหมาะสมที่สุดในการโทรdo_something(a::SparseMatrix, b::SparseMatrix)

วิธีการนี้มีประสิทธิภาพมากกว่า OOP แบบอิงคลาสซึ่งเทียบเท่ากับการส่งแบบอิงตามการสืบทอดบนอาร์กิวเมนต์แรกเท่านั้นหากคุณยอมรับหลักการที่ว่า "วิธีการหนึ่งคือฟังก์ชั่นที่ใช้thisเป็นพารามิเตอร์ตัวแรก" (ทั่วไปเช่นใน Python) บางรูปแบบของการแจกจ่ายหลายอย่างสามารถจำลองใน, พูด, C ++, แต่มีการคัดค้านอย่างมาก

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

อ้างอิงบางส่วน:

http://docs.julialang.org/en/release-0.4/manual/methods/

http://assoc.tumblr.com/post/71454527084/cool-things-you-can-do-in-julia

https://thenewphalls.wordpress.com/2014/03/06/understanding-object-oriented-programming-in-julia-inheritance-part-2/


1

ข้อดีสองประการของวิธีการ OO คือ:

  • βαcalculate_alpha()αcalculate_beta()calculate_alpha()α

  • calculate_f()f(x,y,z)zset_z()zcalculate_f()z

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