ใช้รายการขี้เกียจโดยเฉพาะอย่างยิ่งในภาษาที่คุณไม่รู้จักดี [ปิด]


21

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

งานของคุณคือการเขียนโค้ดเพื่อจัดการรายการที่ขี้เกียจจากนั้นใช้เพื่อใช้อัลกอริทึมนี้ในการสร้างหมายเลข Fibonacci:

ตัวอย่างรหัสอยู่ใน Haskell

let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
 in take 40 fibs

ผล:

[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]

การใช้งานรายการที่ขี้เกียจของคุณควรเป็นไปตามหลักเกณฑ์เหล่านี้:

  • List List เป็นหนึ่งในสามสิ่ง:
    • ไม่มี - รายการว่างเปล่า
      []
    • ข้อด้อย - รายการเดียวจับคู่กับรายชื่อของรายการที่เหลือ:
      1 : [2,3,4,5]
      ( :เป็นผู้ประกอบการ cons ใน Haskell)
    • Thunk - การคำนวณที่เลื่อนออกไปซึ่งสร้างโหนดรายการเมื่อต้องการ
  • สนับสนุนการดำเนินการดังต่อไปนี้:
    • ไม่มี - สร้างรายการที่ว่างเปล่า
    • cons - สร้างเซลล์ข้อเสีย
    • thunk - สร้าง Thunk ให้ฟังก์ชั่นที่ไม่มีการขัดแย้งและส่งกลับศูนย์หรือข้อด้อย
    • แรง - รับโหนดรายการ:
      • หากเป็น Nil หรือ Cons ให้ส่งคืน
      • ถ้าเป็น Thunk ให้เรียกใช้ฟังก์ชันเพื่อรับ Nil หรือ Cons แทนที่ thunk ด้วย Nil หรือ Cons นั้นและส่งคืน
        หมายเหตุ:การเปลี่ยน thunk ที่มีค่าบังคับมันเป็นส่วนสำคัญของคำนิยามของ "ขี้เกียจ" หากข้ามขั้นตอนนี้อัลกอริทึม Fibonacci ด้านบนจะช้าเกินไป
    • empty - ดูว่าโหนด List เป็น Nil หรือไม่ (หลังจากบังคับใช้)
    • หัว (aka "รถยนต์") - รับไอเท็มแรกของรายการ (หรือโยนให้พอดีถ้าไม่มี)
    • tail (aka "cdr") - รับองค์ประกอบหลังส่วนหัวของรายการ (หรือโยนให้พอดีถ้าไม่มี)
    • zipWith - รับฟังก์ชั่นไบนารี (เช่น(+)) และสองรายการ (อาจไม่มีที่สิ้นสุด) ใช้ฟังก์ชันกับรายการที่สอดคล้องกันของรายการ ตัวอย่าง:
      zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
    • รับ - รับหมายเลข N และรายการ (อาจไม่มีที่สิ้นสุด) คว้ารายการ N แรกของรายการ
    • พิมพ์ - พิมพ์รายการทั้งหมดในรายการ สิ่งนี้จะทำงานได้เพิ่มขึ้นเมื่อมีรายการยาวหรือไม่สิ้นสุด
  • fibsใช้ตัวเองในคำนิยามของตัวเอง การตั้งค่าการเรียกซ้ำแบบขี้เกียจนั้นค่อนข้างยุ่งยาก คุณจะต้องทำสิ่งนี้:

    • จัดสรร thunk fibsสำหรับ ปล่อยให้มันอยู่ในสถานะหลอกตาสำหรับตอนนี้
    • กำหนดฟังก์ชั่น thunk fibsซึ่งขึ้นอยู่กับการอ้างอิงถึง
    • อัพเดต thunk ด้วยฟังก์ชัน

    คุณอาจต้องการซ่อนการวางท่อประปานี้โดยการกำหนดฟังก์ชั่นfixที่เรียกฟังก์ชั่นList-return พร้อมกับคืนค่าของมันเอง ลองงีบหลับสั้น ๆ เพื่อให้ความคิดนี้สามารถตั้งค่าได้

  • Polymorphism (ความสามารถในการทำงานกับรายการประเภทใดก็ได้) ไม่จำเป็นต้องใช้ แต่ดูว่าคุณสามารถหาวิธีที่จะใช้สำนวนที่เป็นสำนวนในภาษาของคุณได้ไหม

  • ไม่ต้องกังวลกับการจัดการหน่วยความจำ แม้แต่ภาษาที่มีการรวบรวมขยะมีแนวโน้มที่จะนำพาวัตถุไปด้วยคุณจะไม่ใช้อีก (เช่นใน call stack) ดังนั้นอย่าแปลกใจถ้าโปรแกรมของคุณมีหน่วยความจำรั่วขณะสำรวจรายการที่ไม่สิ้นสุด

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

กฎ:

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

    • Haskell สวยมาก นั่นคือเว้นแต่คุณจะทำสิ่งนี้:

      data List a = IORef (ListNode a)
      data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
      

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

    • งูหลาม:

      • อย่าใช้ itertools
      • เครื่องกำเนิดไฟฟ้านั้นใช้ได้ แต่คุณใช้พวกมันคุณจะต้องหาวิธีในการบันทึกค่าบังคับ

สิ่งที่พฤติกรรมควรจะเป็นเมื่อเรียกzipWithสองรายการที่มีความยาวต่างกัน?
balpha

@ balpha: ฉันเลือกพฤติกรรมของ Haskells: หากรายการใดรายการหนึ่งเป็นศูนย์ให้ส่งกลับศูนย์
FUZxxl

@balpha: ใน Haskell, zipWith จะหยุดเมื่อรายการใดรายการหนึ่งหมด ดังนั้น, zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]. อย่างไรก็ตามสิ่งนี้ไม่สำคัญสำหรับอัลกอริทึม Fibonacci ด้านบนเนื่องจากอาร์กิวเมนต์ทั้งสองของ zipWith เป็นรายการที่ไม่สิ้นสุด
Joey Adams

ความท้าทายนี้มีความประหลาดใจที่ซ่อนอยู่ภายใน: คุณต้องทำสิ่งพิเศษเพื่อนำไปใช้fibsอย่างถูกต้องเนื่องจากขึ้นอยู่กับตัวเอง ฉันอัปเดตคำถามเพื่ออธิบายรายละเอียดเกี่ยวกับการเรียกซ้ำที่ขี้เกียจ FUZxxlคิดออกโดยเขา / เธอ / ตัวเอง
Joey Adams

คุณทำงานแบบค่อยเป็นค่อยไปเมื่อคุณพิมพ์รายการใหญ่หมายถึงอะไร
Lowjacker

คำตอบ:


6

PostScript

ฉันเคยเล่นกับ PostScript มาก่อนแต่ฉันจะไม่บอกว่าฉันรู้ดีเป็นพิเศษ (อันที่จริงการเดาของฉันคือคุณสามารถนับจำนวนผู้คนในโลกที่รู้จัก PostScript ได้ด้วยมือเดียว)

ฉันเบี่ยงเบนไปจากสเป็คของคุณในที่ฟังก์ชั่นที่ใช้ในการสร้าง thunk ได้รับอนุญาตให้กลับมาอีกอัน; forceจะช่วยให้การประเมินจนกว่าผลที่ได้คือหรือnilcons

รายการถูกนำมาใช้เป็นพจนานุกรม:

<< /type /nil >>

<< /type /cons
   /head someValue
   /tail someList >>

<< /type /thunk
   /func evaluationFunction >>

<< /type /dataThunk
   /func evaluationFunction
   /data someValueToBePassedToTheFunction >>

รหัสดังต่อไปนี้ โปรดทราบว่าเรากำลังเขียนทับตัวดำเนินการในตัว (โดยเฉพาะprintฉันไม่ได้ตรวจสอบหากมีมากกว่านั้น); ในการใช้งานจริงนี้จะต้องมีการระวัง แน่นอนว่าจะไม่มีการใช้งานจริงดังนั้นมันก็ดี

ความคิดเห็นก่อนขั้นตอนจะต้องอ่านเป็น

% before2 before1 before0  <| procedure |>  after1 after0

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

% Helper procedure that creates a dictionary with the top two elements as keys
% and the next two elements as values.
%
% value1 value2 key1 key2  <| _twodict |>  << /key1 /value1 /key2 /value2 >>
/_twodict {
    << 5 1 roll    % << value1 value2 key1 key2
    4 2 roll       % << key1 key2 value1 value2
    3 2 roll       % << key1 value1 value2 key2
    exch >>
} def

/nil {
    << /type /nil >>
} def

% item list  <| cons |>  consCell
/cons {
    /head /tail _twodict
    dup /type /cons put
} def

% constructs a thunk from the function, which will be called with no
% arguments to produce the actual list node. It is legal for the function
% to return another thunk.
%
% func  <| thunk |>  lazyList
/thunk {
    /thunk /func /type _twodict
} def

% A dataThunk is like a regular thunk, except that there's an additional
% data object that will be passed to the evaluation function
%
% dataObject func  <| dataThunk |>  lazyList
/dataThunk {
    /data /func _twodict
    dup /type /dataThunk put 
} def

% lazyList  <| force |>  consOrNil
/force {
    dup /type get dup
    /thunk eq
    {
        pop
        dup /func get exec exch copy
        force
        dup /func undef
    }
    {
        /dataThunk eq
        {
            dup dup /data get exch
            /func get exec exch copy
            force
            dup dup /func undef /data undef
        } if
    } ifelse
} def

/empty {
    force
    /type get
    /nil eq
} def

/head {
    force /head get
} def

/tail {
    force /tail get
} def

/print {
    dup empty not
    {
        dup
        head ==
        tail
        print    
    }
    {
        pop
    } ifelse
} def

% sourceList n  <| take |>  resultingList
/take {
    /source /n _twodict
    {
        dup /source get exch    % source data
        /n get 1 sub dup        % source n-1 n-1
        -1 eq
        {
            pop pop nil
        }
        {                       % source n-1
            exch                % n-1 source
            dup head            % n-1 source head
            3 1 roll            % head n-1 source
            tail
            exch take           % head rest
            cons
        } ifelse
    }
    dataThunk
} def

% sourceList1 sourceList2 func  <| zipWith |>  resultList
/zipWith {
    3 1 roll
    2 array astore                  % func [L1 L2] 
    /func /sources _twodict
    {
        dup /sources get aload pop  % data L1 L2
        2 copy empty exch empty or
        {
            pop pop pop nil
        }
        {
            dup head exch tail      % data L1 H2 T2
            3 2 roll
            dup head exch tail      % data H2 T2 H1 T1
            exch                    % data H2 T2 T1 H1
            4 3 roll                % data T2 T1 H1 H2
            5 4 roll /func get      % T2 T1 H1 H2 func
            dup 4 1 roll            % T2 T1 func H1 H2 func
            exec                    % T2 T1 func NEWHEAD
            4 2 roll                % func NEWHEAD T2 T1
            exch 4 3 roll           % NEWHEAD T1 T2 func 
            zipWith cons
        } ifelse
    }
    dataThunk
} def

โหลดสิ่งนี้ลงใน Ghostscript โดยไม่สนใจหน้าที่แสดง - เราทำงานร่วมกับล่ามเท่านั้น นี่คืออัลกอริทึม Fibonacci:

[balpha@localhost lazylist]$ gs lazylist.ps 
GPL Ghostscript 8.71 (2010-02-10)
Copyright (C) 2010 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
GS> /fibs 0 1 { fibs fibs tail { add } zipWith } thunk cons cons def
GS> fibs 40 take print
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
1346269
2178309
3524578
5702887
9227465
14930352
24157817
39088169
63245986
GS>

สองฟังก์ชั่นที่น่าสนใจเพิ่มเติม:

% creates an infinite list that starts with the given value, incrementing
% by one for each additional element
%
% startValue  <| count |>  lazyList
/count {
    {
        dup
        1 add count
        cons
    }
    dataThunk
} def    

% apply the given function to each element of the source list, creating
% a (lazy) list that contains the corresponding results
%
% sourceList function  <| map |> resultList
/map {
    /source /func _twodict
    {
        dup /func get exch
        /source get                 % func source
        dup empty not
        {
            dup head                % func source head
            2 index                 % func source head func
            exec 3 1 roll           % newHead func source
            tail exch map cons
        }
        {
            pop pop nil
        } ifelse
    }
    dataThunk
} def

เริ่มนับที่ 5 คูณแต่ละองค์ประกอบของรายการผลลัพธ์ด้วย 3 และแสดงค่าสิบอันดับแรก:

GS> 5 count { 3 mul } map 10 take print
15
18
21
24
27
30
33
36
39
42

เกี่ยวกับความแตกต่าง: แม้ว่า PostScript จะพิมพ์อย่างรุนแรง แต่ก็อนุญาตให้มีประเภทพจนานุกรมตามตัวอักษรเพื่อให้คุณสามารถโยนสิ่งที่คุณต้องการ:

GS> 1337 [ 42 3.14 ] << /key /value >> (Hello world) 3 count
GS<5> cons cons cons cons 10 take print
1337
[42 3.14]
-dict-
(Hello world)
3
4
5
6
7
8
GS>

โปรดทราบว่าข้อผิดพลาดประเภทนั้นเช่นจากการพยายามเพิ่มสตริงลงในตัวเลขจะเกิดขึ้นในเวลาการประเมินเท่านั้น:

GS> (some string) (another string) nil cons cons
GS<1> 13 27 nil cons cons
GS<2> { add } zipWith      % no error yet
GS<1> print
Error: /typecheck in --add--

น่าอัศจรรย์ (วิธีการ) บันทึกforceค่าที่ส่งคืนหรือไม่
Joey Adams

@JoeyAdams: มันแน่นอน หลังจากการประเมิน thunk copyผู้ประกอบการคัดลอกเนื้อหาของรุ่นที่ประเมินเป็นต้นฉบับเขียนทับ/typeและอาจตั้งค่าอื่น ๆ หลังจากการประเมินซ้ำจนกว่าเราจะมีnilหรือconsมันยัง (ผ่านundef) ลบและในกรณีที่มีผลบังคับใช้/func /dataขั้นตอนสุดท้ายคือไม่จำเป็นอย่างเคร่งครัด ( /funcและ/dataก็จะถูกละเว้น) แต่ออกจากนี้ขั้นตอนที่จะรั่วไหลของหน่วยความจำมากยิ่งขึ้น :)
balpha

6

C

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

วิธีการสร้าง

แรกรับ tarball จากฉันเซิร์ฟเวอร์ มันมี makefile ดังนั้นเพียงแค่เรียกใช้makeเพื่อสร้างมันแล้วmake runเรียกใช้ โปรแกรมจะพิมพ์รายการของหมายเลขฟีโบนัชชี 93 หมายเลขแรก (หลังหมายเลข 94 โอเวอร์โฟลว์จำนวนเต็ม 64 บิตที่ไม่ได้ลงชื่อ)

คำอธิบาย

lazy-list.cโปรแกรมหลักคือแฟ้ม ในไฟล์ส่วนหัวที่สอดคล้องกันฉันกำหนด struct listนั่นคือรายการที่ขี้เกียจของเรา ดูเหมือนว่านี้:

enum cell_kind {
  NIL,
  CONS,
  THUNK
};

typedef enum cell_kind cell_kind;

typedef long int content_t;

struct list {
  cell_kind kind;
  union {
    struct {
      content_t* head;
      struct list* tail;
    } cons;
    struct {
      struct list* (*thunk)(void*);
      /* If you want to give arguments to the thunk, put them in here */
      void* args;
    } thunk;
  } content;
};

สมาชิกkindเป็นแท็ก มันทำเครื่องหมายไม่ว่าเราจะ reched รายชื่อ end ( NIL) เซลล์ที่ได้รับการประเมินแล้ว ( CONS) หรือ thunk ( THUNK) จากนั้นก็มีการรวมกันตาม มันคือ

  • ทั้งเซลล์ที่ประเมินแล้วซึ่งมีค่าและส่วนท้าย
  • หรือ thunk ที่มีฟังก์ชั่นตัวชี้และ struct ที่อาจมีข้อโต้แย้งบางอย่างกับฟังก์ชั่นหากจำเป็น

เนื้อหาของสหภาพถูกยืนยันโดยแท็ก หากแท็กคือNILเนื้อหาของสหภาพจะไม่ได้กำหนด

โดยการกำหนดฟังก์ชั่นผู้ช่วยที่กล่าวถึงในสเปคข้างต้นมักจะสามารถสรุปความหมายรายการจากการใช้งานของมันเช่น คุณสามารถโทรหาnil()รายการที่ว่างเปล่าแทนที่จะสร้างด้วยตัวเอง

ทั้งสามฟังก์ชั่นที่น่าสนใจมากที่สุดคือzipWith, และtake fibonaccisแต่ฉันไม่ต้องการที่จะอธิบายเพราะมันจะคล้ายกับtake zipWithฟังก์ชั่นทั้งหมดที่ทำงานอย่างขี้เกียจมีสามองค์ประกอบ:

  • เสื้อคลุมที่สร้าง thunk
  • ผู้ปฏิบัติงานที่ทำการคำนวณสำหรับหนึ่งเซลล์
  • struct ที่เก็บอาร์กิวเมนต์

ในกรณีของzipWithเหล่านี้เป็นzipWith, และ__zipWith __zipArgsฉันเพิ่งแสดงให้พวกเขาที่นี่โดยไม่มีคำอธิบายเพิ่มเติมฟังก์ชั่นควรจะค่อนข้างชัดเจน:

struct __zipArgs {
  content_t* (*f)(content_t*,content_t*);
  list* listA;
  list* listB;
};

static list* __zipWith(void* args_) {
  struct __zipArgs* args = args_;
  list* listA = args->listA;
  list* listB = args->listB;
  list* listC;

  content_t* (*f)(content_t*,content_t*) = args->f;
  content_t* headA = head(listA);
  content_t* headB = head(listB);
  content_t* headC;

  if (NULL == headA || NULL == headB) {
    free(args);
    return nil();
  } else {
    headC = f(headA, headB);
    args->listA = tail(listA);
    args->listB = tail(listB);
    listC = thunk(__zipWith,args);
    return cons(headC,listC);
  }
}

list* zipWith(content_t* (*f)(content_t*,content_t*),list* listA, list* listB) {
  struct __zipArgs* args = malloc(sizeof(struct __zipArgs));
  args->f = f;
  args->listA = listA;
  args->listB = listB;
  return thunk(__zipWith,args);
}

fibonaccis()ฟังก์ชั่นที่น่าสนใจอื่น ๆ ปัญหาคือเราต้องผ่านตัวชี้ของเซลล์แรกและเซลล์ที่สองไปยัง thunk ของเซลล์ที่สาม แต่เพื่อที่จะสร้างเซลล์เหล่านั้นเราต้องมีตัวชี้ไปยัง thunk ด้วย เพื่อแก้ปัญหานั้นฉันเติมตัวชี้ไปที่ thunk ด้วยอันNULLแรกและเปลี่ยนเป็น thunk หลังจากที่มันถูกสร้างขึ้น นี่คือการฟัง:

static content_t* __add(content_t* a,content_t* b) {
  content_t* result = malloc(sizeof(content_t));
  *result = *a + *b;
  return result;
}

list* fibonaccis() {
  static content_t one_ = 1;
  static content_t zero_ = 0;
  list* one  = cons(&one_,NULL);
  list* two  = cons(&zero_,one);
  list* core = zipWith(__add,one,two);
  one->content.cons.tail = core;
  return two;

การปรับปรุงที่เป็นไปได้

  • วิธีการแก้ปัญหาของฉันไม่ได้ใช้ความหลากหลาย แม้ว่าจะเป็นไปได้ แต่ทักษะ C ของฉันยังไม่เพียงพอที่จะรู้วิธีใช้ แต่ฉันใช้ประเภทcontent_tที่สามารถเปลี่ยนเป็นสิ่งที่เหมาะกับ
  • ใครสามารถดึงอันธพาลออกจากการกำหนดของรายการและใช้มันอย่างเป็นนามธรรม แต่การทำเช่นนั้นจะทำให้โค้ดซับซ้อนขึ้น
  • หนึ่งสามารถปรับปรุงส่วนของรหัสของฉันที่ไม่ดี C.

การส่งที่ดีโดยเฉพาะอย่างยิ่งสำหรับการจับเวลา C ครั้งแรก เกี่ยวกับความแตกต่างถ้าคุณยินดีที่จะจัดสรรเนื้อหาทั้งหมดของคุณบนกองคุณสามารถใช้เป็นชนิดของvoid* content_t
Casey

@Casey: ขอบคุณมาก ฉันคิดว่าจะใช้void*ด้วย แต่ฉันคิดว่ามันจะเลี่ยงที่จะพิมพ์ระบบไกลเกินไป เป็นไปไม่ได้ที่จะใช้เทมเพลตหรือไม่
FUZxxl

C ไม่มีเทมเพลตนั่นคือ C ++ แต่ใช่คุณสามารถใช้เทมเพลต C ++ เพื่อทำให้เป็นแบบทั่วไป
Casey

ฉันไม่รู้ว่าจะใช้มันอย่างไร แต่ฉันเดาว่ามันแค่ว่า C นั้นมีข้อ จำกัด ในแง่ของระบบการพิมพ์ - ฉันไม่สามารถเขียนโปรแกรมนี้ได้โดยไม่ต้องใช้void*และเป็นเพื่อน
FUZxxl

1
“ สมาชิกkindเป็นประเภทของแท็ก” คุณสามารถเรียกมันได้tagเนื่องจากเป็นศัพท์ที่ยอมรับได้ดีสำหรับแนวคิด (เช่นการติดแท็กสหภาพ , Spinless Tagless G-machineในทางกลับกัน "ชนิด" มีความหมายแตกต่างกันใน . Haskell บริบท: ประเภทของประเภทที่ Intมีชนิด*, []มีชนิด* -> *และ(,)มีชนิด* -> * -> *.
โจอี้อดัมส์

5

C ++

นี่คือสิ่งที่ใหญ่ที่สุดที่ฉันเคยเขียนใน C ++ ปกติฉันใช้ Objective-C

มันเป็น polymorphic แต่มันก็ไม่ได้ทำให้อะไรฟรี

mainฟังก์ชั่นของฉัน(และaddฟังก์ชั่น to ZipWith) สิ้นสุดลงเช่นนี้

int add(int a, int b) {return a + b;}

int main(int argc, char **argv) {
    int numFib = 15; // amount of fibonacci numbers we'll print
    if (argc == 2) {
        numFib = atoi(argv[1]);
    }

    // list that starts off 1, 1...
    LazyList<int> fibo = LazyList<int>(new Cons<int>(1,
                     new LazyList<int>(new Cons<int>(1))));
    // zip the list with its own tail
    LazyList<int> *fiboZip = LazyList<int>::ZipWith(add, &fibo, fibo.Tail());
    // connect the begin list to the zipped list
    fibo.Tail() -> ConnectToList(fiboZip);

    // print fibonacci numbers
    int *fibonums = fibo.Take(numFib);    
    for (int i=0; i<numFib; i++) cout << fibonums[i] << " ";

    cout<<endl;

    return 0;
}

สิ่งนี้จะช่วยให้

 ./lazylist-fibo 20
 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 

ชั้นเรียนทำงานเช่นนี้:

make a thunk:    LazyList<T>(new Thunk<T>( function, *args )) 
make empty list: LazyList<T>(new Nil<T>())
make cons:       LazyList<T>(new Cons<T>( car, *cdr ))

list empty:      list.Empty()
list's head:     list.Head()
list's tail:     list.Tail()
zipWith:         LazyList<T>::ZipWith(function, a, b)
take:            list.Take(n)
print:           list.Print()

แหล่งที่มาเต็มรูปแบบ: ที่นี่ มันรกเพราะส่วนใหญ่มันอยู่ในไฟล์ขนาดใหญ่ไฟล์เดียว

แก้ไข: เปลี่ยนลิงค์ (อันเก่าตายแล้ว)


3
ผลงานยอดเยี่ยมและขอขอบคุณสำหรับการ "โยนแบบเต็ม" อย่างแท้จริง :-) ฉันไม่ได้เป็นผู้เชี่ยวชาญ C ++ แต่วิธี C ++ - y เพิ่มเติมในการใช้ Thunk อาจจะใช้ฟังก์ชั่นวัตถุ (aka "functor") (ที่ คือเกิน()ดำเนินการ) void*และเพื่อการใช้งานมรดกเพื่อหลีกเลี่ยงการใช้งาน ดูที่นี่สำหรับตัวอย่างเล็ก ๆ น้อย ๆ ของการทำเช่นนั้น
Joey Adams

ลิงค์แหล่งที่มาแบบเต็มนั้นตายแล้ว คุณสามารถอัปโหลดใหม่ได้หรือไม่ gist.github.comเป็นสถานที่ที่ดีที่จะใส่มัน
Joey Adams

@JoeyAdams: เสร็จแล้ว
marinus

4

หลาม

ไม่ได้ใช้เครื่องกำเนิดไฟฟ้าที่จะใช้รายการเพียงที่จะใช้วิธีการสำหรับการใช้งานร่วมกับ__iter__for

class Node(object):
    def __init__(self, head, tail):
        self.__head__ = head
        self.__tail__ = tail

    def force(self):
        return self

    def empty(self):
        return False

    def head(self):
        return self.__head__

    def tail(self):
        return self.__tail__

    def zip_with(self, func, other):
        def gen_func():
            if other.empty():
                return other
            return Node(func(self.head(), other.head()), self.tail().zip_with(func, other.tail()))
        return Thunk(gen_func)

    def __iter__(self):
        while not self.empty():
            yield self.head()
            self = self.tail()

    def append(self, other):
        while not self.tail().empty():
            self = self.tail()
        self.__tail__ = other

    def take(self, n):
        if n == 0:
            return NullNode()
        else:
            return Node(self.__head__, self.__tail__.take(n - 1))

    def _print(self):
        for item in self:
            print item

class NullNode(Node):
    def __init__(self):
        pass

    def empty(self):
        return True

    def head(self):
        raise TypeError("cannot get head of nil")

    def tail(self):
        raise TypeError("cannot get tail of nil")

    def zip_with(self, func, other):
        return self

    def append(self, other):
        raise TypeError("cannot append to nil")

    def take(self, n):
        return self

class Thunk(Node):
    def __init__(self, func):
        self.func = func

    def force(self):
        node = self.func()
        self.__class__ = node.__class__
        if not node.empty():
            self.__head__ = node.head()
            self.__tail__ = node.tail()
        return self

    def empty(self):
        return self.force().empty()

    def head(self):
        return self.force().head()

    def tail(self):
        return self.force().tail()

    def take(self, n):
        return self.force().take(n)

รายการ Fibonacci ถูกสร้างขึ้นเช่นนี้:

>>> from lazylist import *
>>> fib = Node(0, Node(1, NullNode()))
>>> fib.append(fib.zip_with(lambda a, b: a + b, fib.tail()))
>>> 

1
นี่คือสิ่งที่สวยงาม self.__class__ = node.__class__บรรทัดที่ฉันชอบคือ โปรดทราบว่านี่เป็นข้อผิดพลาด NotImplemented เมื่อมันได้รับถึง 2971215073 (ยาว) ซึ่งเห็นได้ชัดว่าเป็นอาร์กิวเมนต์ที่ไม่ถูกต้องเป็น int. _ เพิ่ม ___ เพื่อสนับสนุนจำนวนเต็มขนาดใหญ่ให้ทำfib.append(fib.zip_with(lambda a,b: a+b, fib.tail()))
Joey Adams

1
ทำไมคุณไม่ผนวกส่วนที่ว่างเปล่าหรืออันธพาลไม่ได้
PyRulez

4

ทับทิม

โปรแกรม Ruby แรกของฉัน เราเป็นตัวแทนโหนดทั้งหมดเป็นอาร์เรย์โดยที่ความยาวอาร์เรย์กำหนดประเภท:

0: empty list
1: thunk (call the single element to get the cons cell)
2: cons cell (1st is head, 2nd is tail)

รหัสนั้นค่อนข้างตรงไปตรงมาโดยมีการแฮ็คเพื่อรีเซ็ตฟังก์ชั่นอันธพาลเพื่อเซ็ตอัพแบบเรียกซ้ำ

def nil_()
  return Array[]
end

def cons(a, b)
  return Array[a, b]
end

def thunk(f)
  return Array[f]
end

def force(x)
  if x.size == 1
    r = x[0].call
    if r.size == 2
      x[0] = r[0]
      x.push(r[1])
    else
      x.pop()
    end
  end
end

def empty(x)
  force(x)
  return x.size == 0
end

def head(x)
  force(x)
  return x[0]
end

def tail(x)
  force(x)
  return x[1]
end

def zipWith(f, a, b)
  return thunk(lambda {
    if empty(a) or empty(b)
      return nil_()
    else
      return cons(f.call(head(a), head(b)), zipWith(f, tail(a), tail(b)))
    end
  })
end

def take(n, x)
  if n == 0
    return nil_()
  else
    return cons(head(x), take(n - 1, tail(x)))
  end
end

def print(x)
  while not empty(x)
    puts x[0]
    x = x[1]
  end
end

def add(x, y)
  return x + y
end

T=thunk(nil)  # dummy thunk function
fibs=cons(0, cons(1, T))
T[0]=zipWith(method(:add), fibs, tail(fibs))[0]  # overwrite thunk function

print(take(40, fibs))

คุณสามารถใช้แทน[...] Array[...]
Lowjacker

3

Google Go

ภาษาที่ค่อนข้างใหม่และฉันเรียนรู้โดยCTRL+Fอิงจากสเป็

package main
import "fmt"

type List struct {
  isNil, isCons, isThunk bool
  head *interface { }
  tail *List
  thunk (func() List)
}

func Nil() List {
  return List { true, false, false, nil, nil, Nil }
}

func Cons(a interface { }, b List) List {
  return List { false, true, false, &a, &b, Nil }
}

func Thunk(f(func() List)) List {
  return List { false, false, true, nil, nil, f }
}

func Force(x List) List {
  if x.isNil { return Nil()
  } else if x.isCons { return Cons(*x.head, *x.tail) }
  return Force(x.thunk())
}

func Empty(x List) bool {
  return Force(x).isNil;
}

func Head(x List) interface { } {
  y := Force(x)
  if y.isNil { panic("No head for empty lists.") }
  return *y.head
}

func Tail(x List) List {
  y := Force(x)
  if y.isNil { panic("No tail for empty lists.") }
  return *y.tail
}

func Take(n int, x List) List {
  if (n == 0) { return Nil() }
  return Thunk(func() List {
    y := Force(x)
    return Cons(*y.head, Take(n - 1, *y.tail))
  })
}

func Wrap(x List) List {
  return Thunk(func() List {
    return x
  })
}

func ZipWith(f(func(interface { }, interface { }) interface { }), a List, b List) List {
  return Thunk(func() List {
    x, y := Force(a), Force(b)
    if x.isNil || y.isNil {
      return Nil()
    }
    return Cons(f(*x.head, *y.head), ZipWith(f, *x.tail, *y.tail))
  });
}

func FromArray(a []interface { }) List {
  l := Nil()
  for i := len(a) - 1; i > -1; i -- {
    l = Cons(a[i], l)
  }
  return l
}

func Print(x List) {
  fmt.Print("[")
  Print1(x)
  fmt.Print("]")
}

func Print1(x List) {
  y := Force(x)
  if y.isCons {
    fmt.Print(Head(y))
    z := Force(Tail(y))
    if z.isCons { fmt.Print(", ") }
    Print1(z)
  }
}

func Plus(a interface { }, b interface { }) interface { } {
  return a.(int) + b.(int)
}

func Fibs() List {

  return Thunk(func() List {
    return Cons(0, Cons(1, Thunk(func() List {
      return ZipWith(Plus, Thunk(Fibs), Tail(Thunk(Fibs)))
    })))
  })
}

func Fibs0() List {
  // alternative method, working
  return Cons(0, Cons(1, Fibs1(0, 1)))
}

func Fibs1(a int, b int) List {
  c := a + b
  return Cons(c, Thunk(func() List { return Fibs1(b, c) }))
}

func CountUp(x int, k int) List {
  return Cons(x, Thunk(func() List {
    return CountUp(x + k, k)
  }))
}

func main() {
  //a := []interface{} { 0, 1, 2, 3 }
  //l, s := FromArray(a), FromArray(a)
  Print(Take(40, Fibs()))
}

ปัญหาได้รับการแก้ไขโดยจัดการกับ thunk-within-a-thunks อย่างไรก็ตามดูเหมือนว่าคอมไพเลอร์ออนไลน์ไม่สามารถใช้ 40 องค์ประกอบอาจเป็นเพราะหน่วยความจำ ฉันจะทดสอบบน Linux ของฉันในภายหลัง

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368runtime: address space conflict: map() = 
throw: runtime: address space conflict

panic during panic

ฉันทดสอบโค้ดด้วยคอมไพเลอร์ออนไลน์เพราะฉันไม่สามารถติดตั้ง Go บน Windows ได้อย่างง่ายดาย


สวยและเรียบง่าย อย่างไรก็ตามแทนที่จะเป็น 3 Bools คุณสามารถใช้แท็กเดียวที่มีค่าที่เป็นไปได้คือค่าคงที่ที่สร้างโดยตัวสร้างiotaค่าคงที่ ดูตัวอย่างในการเขียนโปรแกรมภาษาไปข้อมูลจำเพาะและคำตอบใน StackOverflow
Joey Adams

Fibsฟังก์ชั่นของคุณใช้งานไม่ได้เนื่องจาก Go ใช้การประเมินที่เข้มงวดและFibsเรียกใช้ซ้ำโดยไม่มีเงื่อนไขสิ้นสุด Fibs0/ Fibs1ใช้วิธีการสร้างแบบง่ายมากกว่าอัลกอริทึมที่อธิบายไว้ในโพสต์ของฉันดังนั้นจึงไม่เป็นไปตาม "ข้อกำหนด" fibs = 0 : 1 : zipWith (+) fibs (tail fibs) ฉันปรับปรุงการโพสต์ของฉันที่จะทำอย่างละเอียดในการเรียกซ้ำขี้เกียจซึ่งเป็นสิ่งจำเป็นในการดำเนินการ
Joey Adams

Cons(0, Cons(1, ZipWith(Plus, Thunk(Fibs), Tail(Thunk(Fibs)))))มันออกไปจากความทรงจำ
Ming-Tang

ฉันลองCons(0, Cons(1, Thunk(func() List { return ZipWith(Plus, Thunk(Fibs), Thunk(func() List { return Tail(Fibs()) })) })))และฉันได้รับข้อผิดพลาดที่อยู่หน่วยความจำไม่ถูกต้อง
Ming-Tang

1
เนื่องจากคุณยังคงเรียนรู้ Go: คุณสามารถสร้างโค้ดที่หรูหรากว่านี้ได้โดยใช้ส่วนต่อประสานสำหรับรายการและประเภทแยกสำหรับ Thunks ฯลฯ
cthom06

3

คริสตัล

แม้จะติดตามที่เก็บ GitHub แต่ฉันไม่เคยใช้ Crystal มาก่อนจริงๆ Crystal เป็นตัวแปร Ruby ที่พิมพ์แบบคงที่พร้อมการอนุมานแบบเต็มรูปแบบ แม้ว่าจะมีคำตอบทับทิมอยู่แล้ว แต่การพิมพ์แบบสแตติกของคริสตัลทำให้ฉันใช้ความแตกต่างมากกว่าอาร์เรย์เพื่อแสดงถึงโหนด เนื่องจากคริสตัลไม่อนุญาตให้มีการแก้ไขselfฉันจึงสร้างคลาส wrapper ชื่อNodeที่จะล้อมทุกอย่างอื่นและจัดการ thunks

พร้อมกับการเรียนที่ฉันสร้างฟังก์ชั่นสร้างlnil, และcons thunkฉันไม่เคยใช้ Ruby มาก่อนมากกว่าสคริปต์ 20 บรรทัดก่อนหน้าเช่นกันดังนั้นสิ่งที่บล็อกทำให้ฉันค่อนข้างน้อย

ฉันตามfibฟังก์ชั่นปิดคำตอบไป

class InvalidNodeException < Exception
end

abstract class LazyValue
end

class LNil < LazyValue
    def empty?
        true
    end

    def force!
        self
    end

    def head
        raise InvalidNodeException.new "cannot get head of LNil"
    end

    def tail
        raise InvalidNodeException.new "cannot get tail of Nil"
    end

    def take(n)
        Node.new self
    end
end

class Cons < LazyValue
    def initialize(@car, @cdr)
    end

    def empty?
        false
    end

    def force!
        @cdr.force!
        self
    end

    def head
        @car
    end

    def tail
        @cdr
    end

    def take(n)
        Node.new n > 0 ? Cons.new @car, @cdr.take n-1 : LNil.new
    end
end

class Thunk < LazyValue
    def initialize(&@func : (-> Node))
    end

    def empty?
        raise Exception.new "should not be here!"
    end

    def force!
        @func.call()
    end

    def head
        self.force!.head
    end

    def tail
        self.force!.tail
    end

    def take(n)
        self.force!.take n
    end
end

class Node
    def initialize(@value = LNil.new)
    end

    def empty?
        self.force!
        @value.empty?
    end

    def force!
        @value = @value.force!
        self
    end

    def head
        self.force!
        @value.head
    end

    def tail
        self.force!
        @value.tail
    end

    def take(n)
        self.force!
        return @value.take n
    end

    def print
        cur = self
        while !cur.empty?
            puts cur.head
            cur = cur.tail
        end
    end
end

def lnil
    Node.new LNil.new
end

def cons(x, r)
    Node.new Cons.new x, r
end

def thunk(&f : (-> Node))
    Node.new Thunk.new &f
end

def inf(st=0)
    # a helper to make an infinite list
    f = ->() { lnil }
    f = ->() { st += 1; cons st, thunk &f }
    thunk { cons st, thunk &f }
end

def zipwith(a, b, &f : Int32, Int32 -> Int32)
    thunk { a.empty? || b.empty? ? lnil :
            cons f.call(a.head, b.head), zipwith a.tail, b.tail, &f }
end

def fibs
    # based on the Go answer
    fibs2 = ->(a : Int32, b : Int32) { lnil }
    fibs2 = ->(a : Int32, b : Int32) { cons a+b, thunk { fibs2.call b, a+b } }
    cons 0, cons 1, thunk { fibs2.call 0, 1 }
end

fibs.take(40).print
zipwith(inf, (cons 1, cons 2, cons 3, lnil), &->(a : Int32, b : Int32){ a+b }).print

2

ฉันก้มกฎเล็กน้อยเพราะยังไม่มีวิธีการแก้ปัญหา. NET ที่นี่ - หรือโดยทั่วไปแล้วเป็นวิธีแก้ปัญหา OOP ยกเว้นตัวหนึ่งใน Python ที่ใช้การสืบทอด แต่มันแตกต่างจากโซลูชันของฉันที่จะทำให้ทั้งคู่น่าสนใจ (โดยเฉพาะตั้งแต่ Python อนุญาตให้แก้ไขselfอินสแตนซ์ทำให้การใช้งาน thunk ตรงไปตรงมา)

ดังนั้นนี่คือC # การเปิดเผยอย่างเต็มรูปแบบ: ฉันไม่มีที่อยู่ใกล้กับผู้เริ่มต้นใน C # แต่ฉันไม่ได้สัมผัสภาษาในขณะนี้เนื่องจากฉันไม่ได้ใช้มันในที่ทำงาน

คะแนนเด่น:

  • ทุกชั้นเรียน ( Nil, Cons, Thunk) Listมาจากทั่วไปชั้นฐานนามธรรม

  • Thunkระดับใช้ซองจดหมายรูปแบบ สิ่งนี้จำลองการself.__class__ = node.__class__กำหนดในแหล่งไพ ธ อนเนื่องจากการthisอ้างอิงไม่สามารถแก้ไขได้ใน C #

  • IsEmpty, HeadและTailมีคุณสมบัติ

  • ฟังก์ชั่นที่เหมาะสมทั้งหมดจะถูกนำมาใช้ซ้ำและเกียจคร้าน (ยกเว้นPrintซึ่งไม่สามารถจะขี้เกียจ) โดยการส่งกลับ thunks ตัวอย่างเช่นนี่คือNil<T>.ZipWith:

    public override List<T> ZipWith(Func<T, T, T> func, List<T> other) {
        return Nil();
    }
    

    ... และนี่คือCons<T>.ZipWith:

    public override List<T> ZipWith(Func<T, T, T> func, List<T> other) {
        return Thunk(() => {
            if (other.IsEmpty)
                return Nil();
    
            return Cons(func(Head, other.Head), Tail.ZipWith(func, other.Tail));
        });
    }
    

    แต่น่าเสียดายที่ C # ไม่มีการจัดส่งหลายครั้งมิฉะนั้นฉันสามารถกำจัดifคำสั่งได้เช่นกัน อนิจจาไม่มีลูกเต๋า

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

List<int> fib = null;
fib = List.Cons(0, List.Cons(1,
    List.ZipWith(
        (a, b) => a + b,
        List.Thunk(() => fib),
        List.Thunk(() => fib.Tail))));

(ที่นี่List.Cons, List.ThunkและList.ZipWithมีความสะดวกสบายห่อ.)

ฉันต้องการที่จะเข้าใจว่าทำไมคำจำกัดความที่ง่ายกว่านี้ต่อไปนี้จึงไม่ทำงาน:

List<int> fib = List.Cons(0, List.Cons(1, List.Nil<int>()));
fib = fib.Concat(fib.ZipWith((a, b) => a + b, fib.Tail));

ให้คำจำกัดความที่เหมาะสมConcatของหลักสูตร นี่เป็นสิ่งที่รหัส Python ทำ - แต่มันใช้งานไม่ได้ (= กำลังพอดี)

/ แก้ไข: Joey ได้ชี้ให้เห็นข้อบกพร่องที่ชัดเจนในการแก้ปัญหานี้ อย่างไรก็ตามการแทนที่บรรทัดที่สองด้วย thunk ก็ทำให้เกิดข้อผิดพลาด (Mono segfaults ฉันสงสัยว่าสแต็กล้นซึ่ง Mono จัดการไม่ดี):

fib = List.Thunk(() => fib.Concat(fib.ZipWith((a, b) => a + b, fib.Tail)));

รหัสที่มาเต็มสามารถพบได้เป็นส่วนสำคัญใน GitHub


"น่าเสียดายที่ C # ไม่มีการกระจายหลายครั้ง" - คุณสามารถได้รับผลกระทบจากการใช้กิจกรรมแม้ว่าจะค่อนข้างแฮ็ก
Peter Taylor

สิ่งที่แดกดันเกี่ยวกับการประเมินผลที่ขี้เกียจก็คือมันต้องมีรัฐในการดำเนินการ fib.ZipWithและfib.Tailใช้ของเก่าfibซึ่งยังคงอยู่[0,1]และไม่เปลี่ยนแปลง ดังนั้นคุณจะได้รับ[0,1,1](ฉันคิด) และคุณTakeฟังก์ชั่นไม่ยอมให้คุณใช้เวลาจาก null (ของ Haskell ใช้เวลาไม่แม้ว่า) ลองห่อค่า rvalue ของบรรทัดที่สองด้วย thunk ดังนั้นมันจะอ้างถึงค่าใหม่fibแทนค่าเก่า
Joey Adams

@Peter ใช่; คุณยังสามารถใช้รูปแบบผู้เข้าชมเพื่อใช้การจัดส่งหลายรายการ แต่ฉันต้องการให้วิธีการแก้ปัญหาง่าย ๆ
Konrad Rudolph

@Joey Duh มันชัดเจนมากในตอนนี้ อย่างไรก็ตามโซลูชัน thunk ยังไม่ทำงาน (ดูคำตอบที่อัปเดตแล้ว) แต่ตอนนี้ฉันไม่ว่างที่จะตรวจสอบ
Konrad Rudolph

2

Pico

สำหรับบันทึกวิธีนี้ใช้การแปลของแรงล่าช้าของโครงการตามที่กำหนดไว้ในsrfi-45 และสร้างรายการที่ขี้เกียจด้านบนของที่

{ 
` scheme's srfi-45 begins here `

  _lazy_::"lazy";
  _eager_::"eager";

  lazy(exp())::[[_lazy_, exp]];
  eager(exp)::[[_eager_, exp]];
  delay(exp())::lazy(eager(exp()));

  force(promise)::
    { content:promise[1];
      if(content[1]~_eager_,
        content[2],
        if(content[1]~_lazy_,
          { promise_:content[2]();
            content:promise[1];
            if(content[1]~_lazy_, 
             { content_:promise_[1];
               content[1]:=content_[1];
               content[2]:=content_[2];
               promise_[1]:=content });
            force(promise) })) };

` scheme's srfi-45 ends here `

nil:delay([]);
is_nil(s):size(force(s))=0;
cons(a(),b()):delay([a(),b()]);
head(s):force(s)[1];
tail(s):force(s)[2];

zipWith(f,a,b):
  lazy(if(is_nil(a)|is_nil(b),
         nil,
         cons(f(head(a),head(b)), zipWith(f,tail(a),tail(b)))));

fibs:void;
fibs:=cons(0, cons(1, zipWith(+,fibs,tail(fibs))));

take(c,s):
  lazy(if((c=0)|(is_nil(s)),
         nil,
         cons(head(s),take(c-1,tail(s)))));

print(s):
  { comma(s):
      if(is_nil(s),
        void,
        { display(head(s));
          if(!(is_nil(tail(s))), display(","));
          comma(tail(s)) });
    display("[");
    comma(s);
    display("]");
    void };

print(take(40,fibs))

}

ลักษณะการส่งออกเช่นนี้ ( แต่ขึ้นอยู่กับว่าtpico. เป็น patched มันอาจจะมีคำพูดคู่อื่น ๆ ในมันdisplay. ปกติพิมพ์สตริงด้วยคำพูดเช่นที่ปรากฏทั้งหมด[, ,, ]จะมีคำพูดรอบพวกเขาเช่น"[".)

[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]<void>

เนื่องจากข้อ จำกัด ของประเภทข้อมูลจำนวนเต็มใน tpico สิ่งนี้ล้มเหลวในการคำนวณหมายเลข Fibonacci หมายเลข 45 (หรือ 46)

โปรดทราบว่า tpico 2.0pl11 ใช้งานไม่ได้begin(a,b)(ซึ่งเขียนโดยทั่วไปว่า{a;b}) และifฟังก์ชั่นนี้ไม่ได้เรียกซ้ำ ไม่ต้องพูดถึงว่าต้องใช้เวลาถึง 5 ปีกว่าจะเข้าใจว่าทำไมbeginมันถึงไม่กลับมาซ้ำอีก ในเวลานั้นฉันก็เขียนคำแปลของ srfi-45 ใน Pico มันกลับกลายเป็นว่าbeginกำลังรอค่าbก่อนที่จะกลับมาเมื่อไม่จำเป็นต้องรอ และเมื่อฉันได้รับสิ่งนั้นฉันก็สามารถแก้ไขifได้เหมือนเดิมมีปัญหาเดียวกัน และมีข้อผิดพลาดอื่น ๆ ที่ทำให้ตัวสร้างระดับเมตาmakeไม่ทำงาน

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

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

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