ในฟังก์ชั่นการเขียนโปรแกรม functor คืออะไร?


224

ฉันเจอคำว่า 'Functor' สองสามครั้งในขณะที่อ่านบทความต่าง ๆ เกี่ยวกับฟังก์ชันการเขียนโปรแกรม แต่ผู้เขียนมักจะถือว่าผู้อ่านเข้าใจคำนั้นอยู่แล้ว การมองไปรอบ ๆ บนเว็บได้ให้คำอธิบายทางเทคนิคมากเกินไป (ดูบทความ Wikipedia ) หรือคำอธิบายที่คลุมเครืออย่างไม่น่าเชื่อ (ดูหัวข้อใน Functors ที่เว็บไซต์ ocaml-tutorial )

ใครบางคนสามารถกำหนดคำอธิบายการใช้งานและอาจเป็นตัวอย่างของวิธีการสร้างและใช้งานผู้ใช้

แก้ไข : ในขณะที่ฉันสนใจในทฤษฎีหลังคำ แต่ฉันสนใจทฤษฎีน้อยกว่าฉันในการนำไปปฏิบัติและใช้ประโยชน์จากแนวคิด

แก้ไข 2 : ดูเหมือนว่ามีบางจุดเชื่อมโยงข้ามที่เกิดขึ้น: ฉันหมายถึงเฉพาะ Funisors ของการเขียนโปรแกรมการทำงานไม่ใช่วัตถุฟังก์ชั่นของ C ++


4
ดูเพิ่มเติมที่: adit.io/posts/…
Vlad the Impala

คำตอบที่ดีก็สวยเช่นกัน: stackoverflow.com/a/45149475/1498178
toraritte

หากคุณสนใจในการนำไปใช้และการใช้งานจริงมากกว่าคำศัพท์และทฤษฎีสตราโตสเฟียร์ที่อยู่เบื้องหลังแนวคิดคุณเพียงแค่ใช้สายการบินเดียว: functor แสดงฟังก์ชั่น "map"
Richard Gomes

@RichardGomes IMHO ฉันคิดว่ามันลดบทบาทของ functor ให้เป็นอินเตอร์เฟสแบบ Java อย่างง่ายซึ่งไม่ใช่ functor แปลงสิ่งต่าง ๆ มันสร้างชนิดใหม่จากสิ่งที่มีอยู่ (ใน Haskell) ซึ่งหมายความว่าประเภทนั้นจะถูกแมปด้วย fmapแมปฟังก์ชั่น มีการแมปสองชนิดที่เกี่ยวข้อง วิธีการมองสิ่งต่าง ๆ จะช่วยให้เข้าใจทฤษฎีหมวดหมู่ (ซึ่งเป็นเรื่องทั่วไปมากกว่า) ฉันหมายความว่ามันน่าสนใจที่จะเข้าใจทฤษฎีหมวดหมู่พื้นฐานเพื่อช่วยเราในการเรียนรู้ทฤษฎีหมวดหมู่ทั้งหมดใน Haskell (functor, monads, ... )
Ludovic Kuty

@ VladtheImpala โพสต์บล็อกนั้นยอดเยี่ยม แต่ถึงแม้ว่ามันจะช่วยได้มาก แต่ฉันก็จำไว้ว่านักสร้างหนัง (แผนที่ไป) อีกประเภทหนึ่ง ผมชอบโดยเฉพาะประโยคที่ว่า "functor F ใช้เวลาแต่ละ T ชนิดและแผนที่มันเป็นชนิดใหม่ FT" ในMonads เป็นเช่น Burritos IMHO มันไม่ได้เป็นเพียงบริบท (กล่อง) รอบค่าแม้ว่าจะพิสูจน์ได้จริงที่จะเห็นสิ่งต่าง ๆ เช่นนี้ (Haskell PoV เทียบกับหมวดหมู่ทฤษฎี PoV?)
Ludovic Kuty

คำตอบ:


273

คำว่า "functor" มาจากทฤษฎีหมวดหมู่ซึ่งเป็นสาขาคณิตศาสตร์ทั่วไปที่เป็นนามธรรม มันถูกยืมโดยนักออกแบบของภาษาการทำงานอย่างน้อยสองวิธีที่แตกต่างกัน

  • ในตระกูล ML ภาษา functor เป็นโมดูลที่ใช้หนึ่งโมดูลขึ้นไปเป็นพารามิเตอร์ มันถือเป็นคุณสมบัติขั้นสูงและโปรแกรมเมอร์เริ่มต้นส่วนใหญ่มีปัญหากับมัน

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

    • ประเภทของคีย์ที่จะใช้ในต้นไม้ไบนารี

    • ฟังก์ชั่นการสั่งซื้อทั้งหมดบนกุญแจ

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

    การประยุกต์ใช้ functors ML ก็คือโปรโตคอลเครือข่ายชั้น ลิงค์นี้เป็นบทความที่ยอดเยี่ยมโดยกลุ่ม CMU Fox; มันแสดงวิธีการใช้ functors เพื่อสร้างเลเยอร์โปรโตคอลที่ซับซ้อนมากขึ้น (เช่น TCP) กับประเภทเลเยอร์ที่เรียบง่าย (เช่น IP หรือแม้แต่โดยตรงผ่านอีเธอร์เน็ต) แต่ละชั้นจะถูกนำมาใช้เป็น functor ที่ใช้เป็นพารามิเตอร์ชั้นล่าง โครงสร้างของซอฟต์แวร์สะท้อนให้เห็นถึงวิธีการที่คนคิดเกี่ยวกับปัญหาตรงข้ามกับเลเยอร์ที่มีอยู่ในใจของโปรแกรมเมอร์เท่านั้น ในปี 1994 เมื่องานนี้ตีพิมพ์มันเป็นเรื่องใหญ่

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

  • ใน Haskell และในบางที่เกี่ยวข้องกับภาษาทำงานบริสุทธิ์Functorเป็นระดับประเภท ประเภทเป็นประเภทคลาส (หรือมากกว่านั้นในทางเทคนิคประเภท "เป็นตัวอย่างของ" คลาสประเภท) เมื่อประเภทมีการดำเนินการบางอย่างพร้อมกับพฤติกรรมที่คาดหวัง ประเภทTสามารถเป็นของคลาสได้Functorถ้ามีลักษณะการทำงานคอลเลกชันที่แน่นอน:

    • ประเภทTถูกแปรตามประเภทอื่นซึ่งคุณควรคิดว่าเป็นประเภทองค์ประกอบของคอลเลกชัน ประเภทของคอลเลกชันเต็มแล้วสิ่งที่ชอบT Int, T String, T Boolถ้าคุณกำลังมีจำนวนเต็มสตริงหรือ Booleans ตามลำดับ ถ้าชนิดองค์ประกอบไม่เป็นที่รู้จักก็จะเขียนเป็นพารามิเตอร์ชนิด ในขณะที่aT a

      ตัวอย่างรวมถึงรายการ (องค์ประกอบประเภทศูนย์หรือมากกว่าa), Maybeประเภท (ศูนย์หรือองค์ประกอบหนึ่งประเภทa), ชุดองค์ประกอบประเภทa, อาร์เรย์ขององค์ประกอบประเภทประเภทaต้นไม้ค้นหาทุกชนิดที่มีค่าประเภทaและอื่น ๆ อีกมากมายที่คุณ สามารถคิด

    • คุณสมบัติอื่น ๆ ที่Tต้องพึงพอใจคือถ้าคุณมีฟังก์ชั่นประเภทa -> b(ฟังก์ชั่นในองค์ประกอบ) จากนั้นคุณจะต้องสามารถใช้ฟังก์ชั่นนั้นและผลิตภัณฑ์ฟังก์ชั่นที่เกี่ยวข้องกับคอลเลกชัน คุณทำสิ่งนี้กับโอเปอเรเตอร์fmapซึ่งแชร์ทุกประเภทในFunctorคลาสประเภท ตัวดำเนินการโอเวอร์โหลดจริง ๆ ดังนั้นถ้าคุณมีฟังก์ชั่นที่evenมีประเภทInt -> Boolแล้ว

      fmap even

      เป็นฟังก์ชั่นโอเวอร์โหลดที่สามารถทำสิ่งมหัศจรรย์มากมาย:

      • แปลงรายการจำนวนเต็มเป็นรายการ Booleans

      • แปลงต้นไม้จำนวนเต็มเป็นต้นไม้ของ Booleans

      • แปลงNothingเป็นNothingและJust 7เป็นJust False

      ใน Haskell คุณสมบัตินี้จะแสดงโดยให้ประเภทfmap:

      fmap :: (Functor t) => (a -> b) -> t a -> t b

      ตอนนี้เรามีขนาดเล็กtซึ่งหมายถึง "ประเภทใดก็ได้ในFunctorชั้นเรียน"

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

ตามปกติผู้คนยังคงประดิษฐ์ abstractions ที่มีประโยชน์และคุณอาจต้องการใช้ฟังก์ชั่นการใช้งานซึ่งการอ้างอิงที่ดีที่สุดอาจเป็นกระดาษที่เรียกว่าการเขียนโปรแกรมประยุกต์ด้วยเอฟเฟกต์โดย Conor McBride และ Ross Paterson


7
ฉันเข้าใจทั้ง ML-functors และ Haskell-functors แต่ขาดความเข้าใจในการเชื่อมโยงพวกมันเข้าด้วยกัน ความสัมพันธ์ระหว่างสองสิ่งนี้ในแง่ทฤษฎีหมวดหมู่คืออะไร?
Wei Hu

6
@Wei Hu: ทฤษฎีหมวดหมู่ไม่เคยเข้าใจฉันเลย ที่ดีที่สุดที่ฉันสามารถพูดได้ก็คือความคิดทั้งสามเกี่ยวข้องกับการทำแผนที่
นอร์แมนแรมซีย์

16
ตาม Haskell wiki นี้: en.wikibooks.org/wiki/Haskell/Category_theoryมันเป็นแบบนี้หมวดหมู่คือชุดของวัตถุและ morphisms (ฟังก์ชั่น) โดยที่ morphisms มาจากวัตถุในหมวดหมู่ไปยังวัตถุอื่น ๆ ในประเภทนั้น . ฟังก์ชั่นเป็นฟังก์ชั่นที่แมปวัตถุและมอร์ฟิซึ่มส์จากหมวดหมู่หนึ่งไปยังวัตถุและมอร์ฟิซึ่มส์ในอีกประเภทหนึ่ง อย่างน้อยนั่นคือวิธีที่ฉันเข้าใจ นั่นหมายความว่าสำหรับการเขียนโปรแกรมฉันยังไม่เข้าใจ
พอล

5
@ norman-ramsey คุณเคยดูคณิตศาสตร์เชิงแนวคิดโดย Lawvere และ Schanuel ไหม? ฉันเป็นมือใหม่ทั้งหมดในพื้นที่ แต่หนังสือเล่มนี้สามารถอ่านได้อย่างชัดเจนและ - กล้า - ฉัน - พูด - สนุก (รักคำอธิบายของคุณ)
Ram Rajamony

2
then you have to be able to take that function and product a related function on collectionsคุณหมายถึงproduceแทนproduct?
ปัญหา

64

คำตอบอื่น ๆ ที่นี่จะเสร็จสมบูรณ์ แต่ฉันจะพยายามคำอธิบายการใช้งานของผู้อื่น FP functor ใช้สิ่งนี้เป็นการเปรียบเทียบ:

functor เป็นภาชนะประเภทว่าเมื่ออยู่ภายใต้ฟังก์ชั่นที่แผนที่จาก→ ถัวเฉลี่ยภาชนะประเภทข

ซึ่งแตกต่างจากการใช้ abstract-function-pointer ใน C ++ ตรงนี้ functor ไม่ใช่ฟังก์ชัน แต่มันเป็นสิ่งที่จะทำงานอย่างต่อเนื่องเมื่อยัดเยียดให้ฟังก์ชั่น


3
คอนเทนเนอร์ประเภท bหมายถึง "คอนเทนเนอร์ชนิดเดียวกันกับคอนเทนเนอร์อินพุต แต่ตอนนี้เต็มไปด้วย b's" ดังนั้นถ้าเรามีรายการของกล้วยและเราแมปฟังก์ชั่นที่รับกล้วยและส่งออกสลัดผลไม้ตอนนี้เรามีรายการของสลัดผลไม้ ในทำนองเดียวกันถ้าเรามีต้นไม้กล้วยและเราแมปฟังก์ชันเดียวกันตอนนี้เราจะมีต้นไม้แอปเปิ้ล ต้นไม้ฯลฯและรายการเป็นสองผู้จัดทำที่นี่
Qqwy

3
"นักแสดงเป็นคอนเทนเนอร์ประเภทหนึ่งเมื่ออยู่ภายใต้ฟังก์ชั่น" - จริงๆแล้วมันเป็นวิธีอื่น ๆ - ฟังก์ชั่น (มอร์ฟิซึ่มส์) เป็นเรื่องของนักแสดงที่จะแมปไปยังมอร์ฟิซึ่มอื่น
Dmitri Zaitsev

38

มีสามความหมายที่แตกต่างกันไม่เกี่ยวข้องมากนัก!

  • ใน Ocaml เป็นโมดูลที่กำหนดค่าพารามิเตอร์ ดูคู่มือ ฉันคิดว่าวิธีที่ดีที่สุดในการติดตามพวกเขาคือตัวอย่าง: (เขียนเร็วอาจเป็นบั๊กซี)

    module type Order = sig
        type t
        val compare: t -> t -> bool
    end;;
    
    
    module Integers = struct
        type t = int
        let compare x y = x > y
    end;;
    
    module ReverseOrder = functor (X: Order) -> struct
        type t = X.t
        let compare x y = X.compare y x
    end;;
    
    (* We can order reversely *)
    module K = ReverseOrder (Integers);;
    Integers.compare 3 4;;   (* this is false *)
    K.compare 3 4;;          (* this is true *)
    
    module LexicographicOrder = functor (X: Order) -> 
      functor (Y: Order) -> struct
        type t = X.t * Y.t
        let compare (a,b) (c,d) = if X.compare a c then true
                             else if X.compare c a then false
                             else Y.compare b d
    end;;
    
    (* compare lexicographically *)
    module X = LexicographicOrder (Integers) (Integers);;
    X.compare (2,3) (4,5);;
    
    module LinearSearch = functor (X: Order) -> struct
        type t = X.t array
        let find x k = 0 (* some boring code *)
    end;;
    
    module BinarySearch = functor (X: Order) -> struct
        type t = X.t array
        let find x k = 0 (* some boring code *)
    end;;
    
    (* linear search over arrays of integers *)
    module LS = LinearSearch (Integers);;
    LS.find [|1;2;3] 2;;
    (* binary search over arrays of pairs of integers, 
       sorted lexicographically *)
    module BS = BinarySearch (LexicographicOrder (Integers) (Integers));;
    BS.find [|(2,3);(4,5)|] (2,3);;

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

  • ในภาษาโปรแกรมที่ใช้งานได้เช่น Haskell มันหมายถึงตัวสร้างบางประเภท จะแม่นยำเป็น functor เป็นอุปกรณ์ที่มีf (a -> b) -> (f a -> f b)สิ่งนี้มีต้นกำเนิดในทฤษฎีหมวดหมู่ บทความ Wikipedia ที่คุณเชื่อมโยงกับการใช้งานนี้

    class Functor f where
        fmap :: (a -> b) -> (f a -> f b)
    
    instance Functor [] where      -- lists are a functor
        fmap = map
    
    instance Functor Maybe where   -- Maybe is option in Haskell
        fmap f (Just x) = Just (f x)
        fmap f Nothing = Nothing
    
    fmap (+1) [2,3,4]   -- this is [3,4,5]
    fmap (+1) (Just 5)  -- this is Just 6
    fmap (+1) Nothing   -- this is Nothing

ดังนั้นนี่คือตัวสร้างชนิดพิเศษและไม่มีส่วนเกี่ยวข้องกับ functors ใน Ocaml!

  • ในภาษาที่จำเป็นมันเป็นตัวชี้ไปยังฟังก์ชั่น

<q> แผนที่ </q> ใน 3 บรรทัดสุดท้ายของความคิดเห็นนี้ไม่ควรเป็น <q> fmap </q> ใช่หรือไม่
imz - Ivan Zakharyaschev

1
ฉันได้อ่านเสมอว่า functors เป็นภาชนะบรรจุ แต่นี่เป็นเพียงการทำให้เข้าใจง่าย ในที่สุดคำตอบของคุณคือการระบุลิงก์ที่ขาดหายไป: Functors เป็นประเภทคลาส (ข้อ จำกัด ประเภท) สำหรับประเภทที่กำหนดพารามิเตอร์ (ตัวสร้างประเภท) มันง่ายมาก!

16

ใน OCaml เป็นโมดูลที่กำหนดพารามิเตอร์

หากคุณรู้จัก C ++ ให้นึกถึง OCaml functor เป็นเทมเพลต C ++ มีคลาสเทมเพลตเท่านั้นและ Functionors ทำงานในระดับโมดูล

ตัวอย่างของ functor คือ Map.Make; module StringMap = Map.Make (String);;สร้างโมดูลแผนที่ที่ทำงานกับแผนที่ String-keyed

คุณไม่สามารถทำอะไรอย่าง StringMap ด้วยความหลากหลาย คุณต้องตั้งสมมติฐานบนปุ่ม โมดูลสตริงมีการดำเนินการ (การเปรียบเทียบและอื่น ๆ ) ในประเภทสตริงที่สั่งซื้อทั้งหมดและ functor จะเชื่อมโยงกับการดำเนินงานที่โมดูลสตริงมี คุณสามารถทำสิ่งที่คล้ายกับการเขียนโปรแกรมเชิงวัตถุ แต่คุณมีวิธีเหนือศีรษะโดยวิธีการ


ฉันได้สิ่งนั้นจากเว็บไซต์ ocaml - แต่ฉันไม่เข้าใจว่าการใช้โมดูลแบบพารามิเตอร์จะเป็นอย่างไร
Erik Forbes

4
@ Kornel ใช่สิ่งที่ฉันอธิบายเป็นแนวคิด OCaml แนวคิดอื่นเป็นเพียง "คุณค่าการทำงาน" ซึ่งไม่มีอะไรพิเศษใน FP @Erik ฉันขยายตัวเล็กน้อย แต่เอกสารอ้างอิงนั้นโหลดช้า
Tobu

13

คุณได้รับคำตอบที่ดีพอสมควร ฉันจะขว้างใน:

นักคณิตศาสตร์ในแง่คณิตศาสตร์เป็นฟังก์ชันชนิดพิเศษในพีชคณิต มันเป็นฟังก์ชั่นที่น้อยที่สุดซึ่งแมปพีชคณิตกับพีชคณิตอื่น "Minimality" นั้นแสดงออกมาตามกฎหมายของ functor

มีสองวิธีในการดูนี้ ตัวอย่างเช่นรายการเป็น functors สำหรับบางประเภท นั่นคือเมื่อกำหนดพีชคณิตให้กับประเภท 'a' คุณสามารถสร้างพีชคณิตที่เข้ากันได้ของรายการที่มีสิ่งของประเภท 'a' (ตัวอย่างเช่น: แผนที่ที่นำองค์ประกอบไปยังรายการซิงเกิลตันที่มีมัน: f (a) = [a]) อีกครั้งแนวคิดเกี่ยวกับความเข้ากันได้นั้นแสดงโดยกฎหมายของนักแสดง

ในทางตรงกันข้ามให้ functor f "เหนือ" ประเภท a, (นั่นคือฟะเป็นผลมาจากการใช้ functor f กับพีชคณิตประเภท a) และฟังก์ชั่นจาก g: a -> b เราสามารถคำนวณ functor ใหม่ F = (fmap g) ซึ่งจับคู่กับ f b ในระยะสั้น fmap เป็นส่วนหนึ่งของ F ที่แมป "functor parts" เป็น "functor parts" และ g เป็นส่วนหนึ่งของฟังก์ชั่นที่แมป "algebra parts" เป็น "algebra parts" มันใช้ฟังก์ชั่นนักแสดงและเมื่อเสร็จสมบูรณ์มันก็เป็นนักแสดงด้วย

อาจดูเหมือนว่าภาษาต่าง ๆ ใช้ความคิดที่แตกต่างกันของฟังก์ชั่น แต่ก็ไม่ใช่ พวกเขาใช้แค่ฟังก์ชั่นเหนือ algebras ที่ต่างกัน OCamls มีพีชคณิตของโมดูลและ functors มากกว่าพีชคณิตนั้นให้คุณแนบประกาศใหม่ไปยังโมดูลในทาง "เข้ากันได้"

Haskell functor ไม่ใช่คลาสประเภท มันเป็นชนิดข้อมูลที่มีตัวแปรอิสระที่ตรงกับประเภทของชั้นเรียน หากคุณยินดีที่จะขุดลงในความกล้าของประเภทข้อมูล (โดยไม่มีตัวแปรอิสระ) คุณสามารถตีความประเภทข้อมูลเป็น functor ใหม่เหนือพีชคณิตพื้นฐานได้ ตัวอย่างเช่น:

data F = F Int

isomorphic กับคลาสของ Ints ดังนั้น F ในฐานะตัวสร้างค่าเป็นฟังก์ชันที่จับคู่ Int กับ F Int ซึ่งเป็นพีชคณิตที่เทียบเท่ากัน มันเป็นนักแสดง ในทางกลับกันคุณจะไม่ได้รับ fmap ฟรีที่นี่ นั่นคือสิ่งที่การจับคู่รูปแบบมีไว้สำหรับ

Functors เป็นสิ่งที่ดีสำหรับการ "แนบ" สิ่งต่าง ๆ กับองค์ประกอบของพีชคณิตในวิธีที่เข้ากันได้กับพีชคณิต


8

คำตอบที่ดีที่สุดสำหรับคำถามนั้นอยู่ใน "Typeclassopedia" โดย Brent Yorgey

Monad Reader ฉบับนี้มีคำจำกัดความที่แม่นยำของสิ่งที่ functor รวมถึงคำจำกัดความของแนวคิดอื่น ๆ เช่นเดียวกับไดอะแกรม (Monoid, Applicative, Monad และแนวความคิดอื่น ๆ มีการอธิบายและเห็นในความสัมพันธ์กับ functor)

http://haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf

ตัดตอนมาจาก Typeclassopedia สำหรับ Functor: "สัญชาตญาณง่าย ๆ คือ Functor แสดงถึง" คอนเทนเนอร์ "ของบางประเภทพร้อมกับความสามารถในการใช้ฟังก์ชั่นอย่างสม่ำเสมอกับทุกองค์ประกอบในภาชนะ"

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

ไชโย


7

มีตัวอย่างที่ดีอยู่ในหนังสือ O'Reilly OCaml ที่อยู่ในเว็บไซต์ของ Inria (ซึ่งตอนนี้เขียนลงไปก็น่าเสียดาย) ผมพบว่าตัวอย่างที่คล้ายกันมากในหนังสือเล่มนี้ใช้โดยคาลเทค: รู้เบื้องต้นเกี่ยวกับ OCaml (ลิงค์ PDF) ส่วนที่เกี่ยวข้องคือบทเกี่ยวกับฟังก์ชั่น (หน้า 139 ในหนังสือหน้า 149 ใน PDF)

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

พวกเขายังมีโมดูลที่ใช้ฟังก์ชั่นการเปรียบเทียบเพื่อที่จะเปรียบเทียบสตริงกรณีตาย

การใช้ functor และโมดูลที่ใช้การเปรียบเทียบที่สามารถสร้างโมดูลใหม่ในหนึ่งบรรทัด:

module SSet = MakeSet(StringCaseEqual);;

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

Tobu เปรียบเทียบฟังก์ชั่นกับเทมเพลตใน C ++ ซึ่งฉันคิดว่าค่อนข้างฉลาด


6

ด้วยคำตอบอื่น ๆ และสิ่งที่ฉันจะโพสต์ตอนนี้ฉันจะบอกว่ามันเป็นคำที่ค่อนข้างหนักเกินไป แต่อย่างไรก็ตาม ...

สำหรับคำใบ้เกี่ยวกับความหมายของคำว่า 'functor' ใน Haskell ให้ถาม GHCi:

Prelude> :info Functor
class Functor f where
  fmap :: forall a b. (a -> b) -> f a -> f b
  (GHC.Base.<$) :: forall a b. a -> f b -> f a
        -- Defined in GHC.Base
instance Functor Maybe -- Defined in Data.Maybe
instance Functor [] -- Defined in GHC.Base
instance Functor IO -- Defined in GHC.Base

ดังนั้นโดยทั่วไปนักแสดงใน Haskell คือสิ่งที่สามารถแมปได้ อีกวิธีที่จะบอกว่ามันคือ functor ซึ่งเป็นสิ่งที่ถือได้ว่าเป็นภาชนะที่สามารถขอให้ใช้ฟังก์ชันที่กำหนดให้เปลี่ยนค่ามันมี; ดังนั้นสำหรับรายการที่fmapเกิดขึ้นพร้อมกับmapสำหรับMaybe, fmap f (Just x) = Just (f x), fmap f Nothing = Nothingฯลฯ

หมวดย่อยของประเภทFunctorและส่วนที่เกี่ยวกับFunctors, การใช้งานของผู้เรียนและโมโนคิสของLearn You a Haskell for Great Goodให้ตัวอย่างบางส่วนที่แนวคิดนี้มีประโยชน์ (สรุป: สถานที่มากมาย! :-))

โปรดทราบว่า monad ใด ๆ สามารถถือว่าเป็นนักแสดงและในความเป็นจริงตามที่ Craig Stuntz ชี้ให้เห็นฟังก์ชั่นที่ใช้บ่อยที่สุดมักจะเป็น monads ... OTOH มันสะดวกในบางครั้งที่จะทำให้เป็นตัวอย่างของประเภทของ Functor โดยไม่ต้องไปลำบากในการทำให้เป็น Monad (เช่นในกรณีที่ZipListมาจากที่Control.Applicativeกล่าวถึงในหน้าใดหน้าหนึ่งดังกล่าว )


5

นี่เป็นบทความเกี่ยวกับ functors จากการเขียนโปรแกรม POV , ตามมาด้วยมากขึ้นโดยเฉพาะวิธีที่พวกเขาปรากฏในการเขียนโปรแกรมภาษา

การใช้งานจริงของ functor นั้นอยู่ใน monad และคุณจะพบบทเรียนมากมายเกี่ยวกับ monads ถ้าคุณมองหามัน


1
"การใช้งานจริงของนักแสดงละครอยู่ใน Monad" - ไม่เพียง แต่ Monads ทั้งหมดเป็น functors แต่มีการใช้งานมากมายสำหรับ functors ที่ไม่ใช่ monad
amindfv

1
ฉันจะบอกว่าการเรียนพระเพื่อใช้ฟังก์ชั่นก็เหมือนกับการออมเพื่อซื้อโรลเพื่อซื้อของชำ
Marco Faustinelli

5

ในความคิดเห็นต่อคำตอบที่ได้รับการโหวตสูงสุดผู้ใช้Wei Huถาม:

ฉันเข้าใจทั้ง ML-functors และ Haskell-functors แต่ขาดความเข้าใจในการเชื่อมโยงพวกมันเข้าด้วยกัน ความสัมพันธ์ระหว่างสองสิ่งนี้ในแง่ทฤษฎีหมวดหมู่คืออะไร?

หมายเหตุ : ฉันไม่รู้ ML ดังนั้นโปรดให้อภัยและแก้ไขข้อผิดพลาดที่เกี่ยวข้อง

เริ่มแรกสมมติว่าเราทุกคนคุ้นเคยกับคำจำกัดความของ 'หมวดหมู่' และ 'functor'

คำตอบที่มีขนาดกะทัดรัดจะเป็นที่ "Haskell-functors" คือ (endo-) functors F : Hask -> Haskขณะที่ "ML-functors" เป็น G : ML -> ML'functors

นี่Haskคือหมวดหมู่ที่สร้างขึ้นโดย Haskell ประเภทและฟังก์ชั่นระหว่างพวกเขาและในทำนองเดียวกันMLและML'เป็นหมวดหมู่ที่กำหนดโดยโครงสร้าง ML

หมายเหตุ : มีปัญหาทางเทคนิคเกี่ยวกับการสร้างHaskหมวดหมู่ แต่มีวิธีแก้ไข

จากมุมมองเชิงทฤษฎีหมวดหมู่ซึ่งหมายความว่าHask-functor เป็นแผนที่Fประเภท Haskell:

data F a = ...

พร้อมกับแผนที่fmapของฟังก์ชั่น Haskell:

instance Functor F where
    fmap f = ...

ML นั้นเหมือนกันมากแม้ว่าจะไม่มีสิ่งใดที่เป็นfmapนามธรรมที่เป็นที่ยอมรับฉันก็เลยลองกำหนดหนึ่ง:

signature FUNCTOR = sig
  type 'a f
  val fmap: 'a -> 'b -> 'a f -> 'b f
end

นั่นคือfแผนที่ML-type และfmapmaps ML-functions ดังนั้น

functor StructB (StructA : SigA) :> FUNCTOR =
struct
  fmap g = ...
  ...
end

เป็นนักF: StructA -> StructBแสดง


5

"Functor คือการทำแผนที่วัตถุและ morphisms ที่รักษาองค์ประกอบและเอกลักษณ์ของหมวดหมู่"

ให้กำหนดหมวดหมู่คืออะไร

มันเป็นพวงของวัตถุ!

วาดจุดสองสามจุด (สำหรับตอนนี้ 2 จุดจุดหนึ่งคือ 'อีก' คือ 'b') ภายในวงกลมและตั้งชื่อวงกลมที่ A (หมวดหมู่) เป็นตอนนี้

หมวดหมู่นี้ถืออะไร?

องค์ประกอบระหว่างวัตถุและฟังก์ชั่นเอกลักษณ์สำหรับทุกวัตถุ

ดังนั้นเราต้องแมปวัตถุและรักษาองค์ประกอบหลังจากใช้ Functor ของเรา

ให้จินตนาการ 'A' เป็นหมวดหมู่ของเราที่มีวัตถุ ['a', 'b'] และมี morphism a -> b

ตอนนี้เราต้องกำหนด functor ที่สามารถแมปวัตถุและ morphisms เหล่านี้ในหมวดหมู่ 'B' อีกประเภทหนึ่ง

ให้บอกว่านักแสดงเรียกว่า 'อาจจะ'

data Maybe a = Nothing | Just a

ดังนั้นหมวดหมู่ 'B' จะมีลักษณะเช่นนี้

กรุณาวาดวงกลมอีกวง แต่คราวนี้มี 'อาจ' และ 'อาจ b' แทน 'a' และ 'b'

ทุกอย่างดูดีและวัตถุทั้งหมดถูกแมป

'a' กลายเป็น 'บางที a' และ 'b' กลายเป็น 'อาจ b'

แต่ปัญหาคือเราต้องแมปมอร์ฟิซึ่มส์จาก 'a' ถึง 'b' ด้วย

นั่นหมายถึงมอร์ฟิซึ่มส์ -> b ใน 'A' ควรแมปกับมอร์ฟิซึ่มส์ 'บางทีอาจเป็น' -> 'บางทีข'

morphism จาก a -> b เรียกว่า f จากนั้น morphism จาก 'บางที a' -> 'บางที b' เรียกว่า 'fmap f'

ทีนี้มาดูกันว่าฟังก์ชั่น 'f' กำลังทำอะไรใน 'A' และดูว่าเราสามารถทำซ้ำมันใน 'B' ได้ไหม

นิยามฟังก์ชั่นของ 'f' ใน 'A':

f :: a -> b

f รับ a และส่งคืน b

คำนิยามฟังก์ชั่นของ 'f' ใน 'B':

f :: Maybe a -> Maybe b

f ใช้เวลาบางทีและกลับบางที b

ให้ดูวิธีการใช้ fmap เพื่อแมปฟังก์ชั่น 'f' จาก 'A' กับฟังก์ชั่น 'fmap f' ใน 'B'

คำจำกัดความของ fmap

fmap :: (a -> b) -> (Maybe a -> Maybe b)
fmap f Nothing = Nothing
fmap f (Just x) = Just(f x)

แล้วเรามาทำอะไรที่นี่?

เรากำลังใช้ฟังก์ชั่น 'f' กับ 'x' ซึ่งเป็นประเภท 'a' จับคู่รูปแบบพิเศษของ 'ไม่มีอะไร' Functor Maybeมาจากคำจำกัดความของ

ดังนั้นเราจึงแมปวัตถุของเรา [a, b] และ morphisms [f] จากหมวดหมู่ 'A' ถึงหมวดหมู่ 'B'

นั่นเป็นนักแสดง!

ป้อนคำอธิบายรูปภาพที่นี่


คำตอบที่น่าสนใจ ฉันต้องการเติมเต็มให้กับMonads เป็นเหมือน burritos (คำตอบที่ตลกกับAbstraction, สัญชาตญาณและ "monad tutorial fallacy" ) และประโยคของเขา "นักแสดง F นำแต่ละประเภท T และแมปไปยัง FT ประเภทใหม่" หรือที่รู้จัก . ฟังก์ชั่นการเขียนโปรแกรมและทฤษฎีหมวดหมู่ - หมวดหมู่และ Funisorsก็มีประโยชน์เช่นกัน
Ludovic Kuty

3

ภาพรวมคร่าวๆ

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

ตัวอย่างของ functors รวมถึงอาร์เรย์ "บางที" และ "อย่างใดอย่างหนึ่ง" functors ฟิวเจอร์ส (ดูเช่นhttps://github.com/Avaq/Fluture ) และอื่น ๆ อีกมากมาย

ภาพประกอบ

พิจารณาฟังก์ชั่นการสร้างชื่อเต็มของบุคคลจากชื่อและนามสกุล เราสามารถนิยามว่ามันfullName(firstName, lastName)เป็นฟังก์ชั่นของสองข้อโต้แย้งซึ่งจะไม่เหมาะสำหรับ functors ที่จัดการกับฟังก์ชั่นของการขัดแย้งเดียว เพื่อแก้ไขเรารวบรวมข้อโต้แย้งทั้งหมดในวัตถุเดียวnameซึ่งตอนนี้กลายเป็นอาร์กิวเมนต์เดียวของฟังก์ชั่น:

// In JavaScript notation
fullName = name => name.firstName + ' ' + name.lastName

แล้วถ้าเรามีคนมากมายในอาเรย์ล่ะ? แทนที่จะไปดูรายการด้วยตนเองเราสามารถใช้ฟังก์ชั่นของเราได้อีกครั้งfullNameผ่านmapวิธีการที่กำหนดไว้สำหรับอาร์เรย์ด้วยรหัสบรรทัดเดียวขนาดสั้น:

fullNameList = nameList => nameList.map(fullName)

และใช้มันเหมือน

nameList = [
    {firstName: 'Steve', lastName: 'Jobs'},
    {firstName: 'Bill', lastName: 'Gates'}
]

fullNames = fullNameList(nameList) 
// => ['Steve Jobs', 'Bill Gates']

สิ่งนี้จะใช้ได้เมื่อทุกรายการในของเราnameListเป็นวัตถุที่ให้ทั้งคุณสมบัติfirstNameและ lastNameแต่ถ้าวัตถุบางอย่างไม่ได้ (หรือแม้กระทั่งไม่ได้วัตถุเลย)? เพื่อหลีกเลี่ยงข้อผิดพลาดและทำให้รหัสปลอดภัยยิ่งขึ้นเราสามารถห่อวัตถุของเราเป็นMaybeประเภท (se เช่นhttps://sanctuary.js.org/#maybe-type ):

// function to test name for validity
isValidName = name => 
    (typeof name === 'object') 
    && (typeof name.firstName === 'string')
    && (typeof name.lastName === 'string')

// wrap into the Maybe type
maybeName = name => 
    isValidName(name) ? Just(name) : Nothing()

โดยที่Just(name)container เป็นชื่อที่ถูกต้องเท่านั้นและNothing()เป็นค่าพิเศษที่ใช้สำหรับทุกอย่าง ตอนนี้แทนที่จะขัดจังหวะ (หรือลืม) เพื่อตรวจสอบความถูกต้องของข้อโต้แย้งของเราเราสามารถนำfullNameฟังก์ชันเดิมของเรากลับมาใช้ใหม่โดยใช้รหัสบรรทัดเดียวอีกบรรทัดโดยใช้mapวิธีการอีกครั้งคราวนี้เป็นประเภทประเภทบางที:

// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)

และใช้มันเหมือน

justSteve = maybeName(
    {firstName: 'Steve', lastName: 'Jobs'}
) // => Just({firstName: 'Steve', lastName: 'Jobs'})

notSteve = maybeName(
    {lastName: 'SomeJobs'}
) // => Nothing()

steveFN = maybeFullName(justSteve)
// => Just('Steve Jobs')

notSteveFN = maybeFullName(notSteve)
// => Nothing()

ทฤษฎีหมวดหมู่

Functorในทฤษฎีประเภทคือแผนที่ระหว่างสองประเภทเคารพองค์ประกอบของ morphisms ของพวกเขา ในภาษาคอมพิวเตอร์หมวดหมู่ความสนใจหลักคือวัตถุที่มีประเภท (ชุดค่าบางอย่าง) และฟังก์ชันที่มีmorphismsเป็นf:a->bประเภทจากประเภทหนึ่งaไปยังอีกประเภทbหนึ่ง

ตัวอย่างเช่นรับaเป็นStringชนิดbNumber และfฟังก์ชันการแม็พสตริงเข้ากับความยาว:

// f :: String -> Number
f = str => str.length

นี่คือa = Stringชุดของสตริงทั้งหมดและb = Numberชุดของตัวเลขทั้งหมด ในแง่นั้นทั้งaและbแสดงวัตถุในหมวดหมู่ชุด (ซึ่งเกี่ยวข้องอย่างใกล้ชิดกับหมวดหมู่ของประเภทด้วยความแตกต่างที่ไม่จำเป็นที่นี่) ในหมวดหมู่ชุดmorphismsระหว่างสองชุดเป็นฟังก์ชันทั้งหมดอย่างแม่นยำจากชุดแรกเป็นชุดที่สอง ฟังก์ชันความยาวของเราตรงfนี้คือมอร์ฟิซึ่มส์จากเซตของสตริงไปยังชุดตัวเลข

ในฐานะที่เราพิจารณาเฉพาะหมวดหมู่ชุดที่เกี่ยวข้องFunctorsจากมันเข้าไปในตัวของมันเองมีแผนที่จะส่งวัตถุวัตถุและ morphisms เพื่อ morphisms ที่ตอบสนองกฎหมายเกี่ยวกับพีชคณิตบางอย่าง

ตัวอย่าง: Array

Arrayอาจหมายถึงหลายสิ่งหลายอย่าง แต่เพียงสิ่งเดียวที่เป็น Functor - ประเภทสร้าง, การทำแผนที่ชนิดaเข้าไปในประเภทของอาร์เรย์ทุกประเภท[a] aตัวอย่างเช่นArrayfunctor จะแมปประเภทString เป็นประเภท[String](ชุดของอาร์เรย์ทั้งหมดของสตริงที่มีความยาวโดยพลการ) และชุดประเภทNumberเป็นประเภทที่สอดคล้องกัน[Number](ชุดของตัวเลขทั้งหมดในอาร์เรย์)

เป็นสิ่งสำคัญที่จะไม่ทำให้แผนที่ Functor สับสน

Array :: a => [a]

a -> [a]กับซึ่มส์ นักแสดงจะทำการแมป (ประเภท) ประเภทaเป็นประเภท[a]หนึ่งกับอีกสิ่งหนึ่ง แต่ละประเภทเป็นชุดขององค์ประกอบจริง ๆ แล้วไม่มีความเกี่ยวข้องกันที่นี่ ในทางตรงกันข้ามมอร์ฟิซึ่มส์เป็นฟังก์ชั่นจริงระหว่างชุดเหล่านั้น ตัวอย่างเช่นมี morphism ตามธรรมชาติ (ฟังก์ชั่น)

pure :: a -> [a]
pure = x => [x]

ซึ่งจะส่งค่าไปยังอาร์เรย์ 1 องค์ประกอบพร้อมกับค่านั้นเป็นรายการเดียว ฟังก์ชั่นนั้นไม่ได้เป็นส่วนหนึ่งของArrayFunctor! จากมุมมองของ functor นี้pureเป็นเพียงฟังก์ชั่นเหมือนกันไม่มีอะไรพิเศษ

ในขณะที่ArrayFunctor มีส่วนที่สอง - ส่วนมอร์ฟิซึ่มส์ ซึ่งแผนที่ morphism f :: a -> bเป็น morphism [f] :: [a] -> [b]:

// a -> [a]
Array.map(f) = arr => arr.map(f)

นี่arrเป็นอาร์เรย์ของความยาวโดยพลการที่มีค่าของชนิดใด ๆaและarr.map(f)เป็นอาร์เรย์ของความยาวเช่นเดียวกันกับค่าประเภทbซึ่งรายการนี้เป็นผลของการประยุกต์ใช้กับรายการของf arrเพื่อให้เป็น functor กฎทางคณิตศาสตร์ของการทำแผนที่ตัวตนกับตัวตนและองค์ประกอบของการแต่งจะต้องถือซึ่งง่ายต่อการตรวจสอบในArrayตัวอย่างนี้


2

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

ตัวอย่างคืออินเตอร์เฟส Runnable ใน Java ซึ่งมีเพียงวิธีเดียวเท่านั้น: run

ลองพิจารณาตัวอย่างนี้ก่อนใน Javascript ซึ่งมีฟังก์ชั่นชั้นหนึ่ง:

[1, 2, 5, 10].map(function(x) { return x*x; });

ผลลัพธ์: [1, 4, 25, 100]

วิธีการที่แผนที่ใช้ฟังก์ชั่นและส่งกลับอาร์เรย์ใหม่ที่มีองค์ประกอบแต่ละอย่างเป็นผลมาจากการประยุกต์ใช้ฟังก์ชั่นนั้นเป็นค่าที่ตำแหน่งเดียวกันในอาร์เรย์เดิม

ในการทำสิ่งเดียวกันคือ Java โดยใช้ Functor คุณต้องกำหนดอินเตอร์เฟสก่อน:

public interface IntMapFunction {
  public int f(int x);
}

จากนั้นถ้าคุณเพิ่มคลาสคอลเลกชันที่มีฟังก์ชั่นแผนที่คุณสามารถทำได้:

myCollection.map(new IntMapFunction() { public int f(int x) { return x * x; } });

สิ่งนี้ใช้คลาสย่อยในบรรทัดของ IntMapFunction เพื่อสร้าง Functor ซึ่งเทียบเท่ากับ OO ของฟังก์ชันจากตัวอย่าง JavaScript ก่อนหน้านี้

การใช้ Functors ให้คุณใช้เทคนิคการใช้งานในภาษา OO แน่นอนว่าบางภาษา OO ยังรองรับฟังก์ชั่นโดยตรงดังนั้นจึงไม่จำเป็น

การอ้างอิง: http://en.wikipedia.org/wiki/Function_object


ที่จริงแล้ว "function object" ไม่ใช่คำอธิบายที่ถูกต้องของ functor เช่นArrayเป็น functor แต่Array(value)ให้ 1 อาร์เรย์เท่านั้น
Dmitri Zaitsev

0

KISS: functor เป็นวัตถุที่มีวิธีการแมป

อาร์เรย์ใน JavaScript ใช้แผนที่และเป็นฟังก์ชัน คำสัญญาลำธารและต้นไม้มักจะใช้แผนที่ในภาษาที่ใช้งานได้และเมื่อพวกมันทำพวกเขาจะถือเป็นหน้าที่ วิธีการแมปของ functor ใช้เนื้อหาของตัวเองและแปลงแต่ละรายการโดยใช้ callback การแปลงที่ส่งผ่านไปยังแผนที่และส่งคืน functor ใหม่ซึ่งมีโครงสร้างเป็น functor แรก แต่ด้วยค่าที่เปลี่ยนแปลง

src: https://www.youtube.com/watch?v=DisD9ftUyCk&feature=youtu.be&t=76


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

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

1
ใช่พวกเขาอาจสับสน แต่: คลาสใช้อินเทอร์เฟซ (พวกเขา 'เติม' ช่องว่างที่กำหนดไว้ในวิธีการเชื่อมต่อในคำอื่น ๆ : พวกเขาเปลี่ยนแนวทางนามธรรมของอินเทอร์เฟซในแนวทางคอนกรีตที่สามารถทันที (ยกโทษ Pun) อินสแตนซ์ สำหรับ 'คลาสที่มีพฤติกรรมเหมือนวัตถุ': แท้จริงในภาษา OOP เช่น Ruby คลาสจะเป็นอินสแตนซ์ของคลาส 'Class' มันเต่าตลอดทางลง
Qqwy

Arrayประเภทการก่อสร้างกำหนด functor เดียว อินสแตนซ์ของมันถูกเรียกอีกอย่างว่า "อาร์เรย์" แต่ไม่ใช่ฟังก์ชัน คำอธิบายที่นี่ควรทำให้แม่นยำยิ่งขึ้น
Dmitri Zaitsev

@DmitriZaitsev คุณช่วยอธิบายได้ไหม? ดังนั้นสิ่งที่คุณพูดคือกรณีที่ไม่ใช่หน้าที่? ฉันไม่เห็นความรู้สึกนั้นเพราะคุณได้รับ functor ใหม่โดยการทำแผนที่มากกว่าหนึ่ง
soundyogi

-4

ในทางปฏิบัติ functor หมายถึงวัตถุที่ใช้ตัวดำเนินการการโทรใน C ++ ใน ocaml ฉันคิดว่า functor หมายถึงสิ่งที่ใช้โมดูลเป็นอินพุทและเอาท์พุทโมดูลอื่น


-6

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

ใน C ++:

นี่คือวิธีที่คุณเขียนฟังก์ชั่น

void foo()
{
    cout << "Hello, world! I'm a function!";
}

นี่คือวิธีที่คุณเขียน functor

class FunctorClass
{
    public:
    void operator ()
    {
        cout << "Hello, world! I'm a functor!";
    }
};

ตอนนี้คุณสามารถทำได้:

foo(); //result: Hello, World! I'm a function!

FunctorClass bar;
bar(); //result: Hello, World! I'm a functor!

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


12
ไม่ฟังก์ชั่นเหล่านั้นไม่ใช่แนวคิดทฤษฎีประเภทที่ใช้โดยภาษา FP
Tobu

1
ฉันสามารถดูว่ามีใครพิสูจน์ได้ว่าเป็นไปFunctorClassตามกฎหมาย Functor ข้อแรก แต่คุณสามารถร่างหลักฐานสำหรับกฎข้อที่สองได้หรือไม่? ฉันไม่ค่อยเห็นมันเลย
Jörg W Mittag

3
เฮ้พวกคุณพูดถูก ฉันใช้ความพยายามอย่างมากในการแก้ปัญหา "เว็บได้ให้คำอธิบายทางเทคนิคอย่างล้นเหลือ" และพยายามหลีกเลี่ยง "ในตระกูล ML ของภาษา functor เป็นโมดูลที่ใช้โมดูลหนึ่งตัวหรือมากกว่าเป็นพารามิเตอร์" อย่างไรก็ตามคำตอบนี้คือไม่ดี oversimplified และ underspecified ฉันอยากจะ ragedelete มัน แต่ฉันจะปล่อยให้คนรุ่นต่อไปในอนาคตที่จะสั่นหัวของพวกเขาที่ :)
แมตต์

ฉันดีใจที่คุณออกจากคำตอบและความคิดเห็นเพราะมันช่วยให้กรอบปัญหา ขอบคุณ! ฉันมีปัญหาในการที่คำตอบส่วนใหญ่จะเขียนในแง่ของ Haskell หรือ OCaml และสำหรับฉันนั่นเป็นเพียงเล็กน้อยเช่นการอธิบายจระเข้ในแง่ของจระเข้
Rob

-10

Functor ไม่ได้เกี่ยวข้องกับการเขียนโปรแกรมฟังก์ชั่นโดยเฉพาะ มันเป็นเพียง "ตัวชี้" ไปยังฟังก์ชั่นหรือวัตถุบางอย่างที่สามารถเรียกได้ว่ามันจะเป็นฟังก์ชั่น


8
มีแนวคิด FP เฉพาะของ functor (จากทฤษฎีหมวดหมู่) แต่คุณคิดถูกว่าคำเดียวกันนี้ยังใช้กับสิ่งอื่นในภาษาที่ไม่ใช่ FP ด้วย
Craig Stuntz

คุณแน่ใจหรือว่าตัวชี้ฟังก์ชั่นเป็น Functors? ฉันไม่เห็นว่าตัวชี้ของฟังก์ชั่นตอบสนองกฎหมายทั้งสองของ Functor ได้อย่างไรโดยเฉพาะอย่างยิ่งกฎหมาย Functor ที่สอง (การรักษาองค์ประกอบของมอร์ฟิซึ่มส์) คุณมีข้อพิสูจน์เรื่องนี้หรือไม่? (เป็นเพียงร่างคร่าวๆ)
Jörg W Mittag
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.