การเข้าถึงความลึกของสแต็กการโทรใน Scheme

เพื่อแสดงให้เห็นถึงประสิทธิภาพของการเรียกซ้ำส่วนท้าย ฉันต้องการวิธีเข้าถึงความลึกของ call stack แบบไดนามิกใน Scheme

มีวิธีการทำเช่นนี้หรือไม่? ถ้าไม่ มีวิธีการทำเช่นนี้ในภาษาการทำงานหลักอื่น ๆ (OCaml, Haskell ฯลฯ ) หรือไม่?


person Huck Bennett    schedule 18.06.2015    source แหล่งที่มา


คำตอบ (2)


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

#lang racket
;;; This module holds the tools for keeping track of
;;; the current depth.
(module depth racket
  (provide (rename-out [depth-app #%app])
           current-depth)

  (define (extract-current-continuation-marks key)
    (continuation-mark-set->list (current-continuation-marks) key))

  (define (current-depth)
    (car (extract-current-continuation-marks 'depth)))

  (define-syntax (depth-app stx)
    (syntax-case stx ()
      [(_depth-app proc args ...)
       #'(let ([p  (with-continuation-mark 'depth (+ (current-depth) 1) 
                     proc)]
               [as (with-continuation-mark 'depth (+ (current-depth) 1)
                     (list args ...))])
           (apply p as))])))

;;; Importing the #%app from the depth module will override
;;; the standard application to use depth-app which keeps
;;; track of the depth.
(require 'depth)

(with-continuation-mark 'depth 0  ; set the initial depth to 0
  (let ()
    ;;; Example:  foo is tail recursive
    (define (foo n)
      (displayln (list 'foo n (current-depth)))
      (if (< n 5)
          (foo (+ n 1))
          'done))

    ;;; bar is not tail recursive
    (define (bar n)
      (displayln (list 'bar n (current-depth)))
      (if (< n 5)
          (cons n (bar (+ n 1)))
          '()))

    ;;; Call the examples
    (foo 0)
    (bar 0)))

ผลลัพธ์คือ:

(foo 0 2)
(foo 1 2)
(foo 2 2)
(foo 3 2)
(foo 4 2)
(foo 5 2)
(bar 0 2)
(bar 1 3)
(bar 2 4)
(bar 3 5)
(bar 4 6)
(bar 5 7)
'(0 1 2 3 4)

ผลลัพธ์แสดงว่าความลึกไม่เพิ่มขึ้นใน foo และเพิ่มขึ้นใน bar

person soegaard    schedule 20.06.2015
comment
พูดอย่างเคร่งครัด นี่ไม่ใช่ call stack จริง เนื่องจากการเพิ่มประสิทธิภาพการเรียกหางและการแปลง CPS ที่จำเป็นของ Scheme แต่จะแสดงความลึกของการซ้อนการโทรโดยการบังคับให้รวมการโทรด้วยรหัสก่อน/ไปรษณีย์ ฉันไม่แน่ใจ 100% ว่าจะดำเนินการอย่างไร แต่มีโอกาสสูงที่โค้ดประเภทนี้จะป้องกันไม่ให้โค้ดแบบเรียกซ้ำส่วนท้ายถูกคอมไพล์เป็นการวนซ้ำ (เพราะมันบังคับให้การประเมินย้อนกลับและกู้คืนตัวนับความลึกของการโทรหลังจากนั้น งานทั้งหมดเสร็จสิ้นแล้ว) - person sjamaan; 25.06.2015
comment
การบังคับใช้หมายถึงอะไรในบริบทนี้ - person soegaard; 25.06.2015
comment
@sjamaan Btw - โปรดทราบว่า (apply p as) อยู่ในตำแหน่งส่วนท้าย เอกสารกล่าวถึง apply ว่า proc ที่กำหนดถูกเรียกในตำแหน่งส่วนท้ายที่เกี่ยวข้องกับการเรียกใช้ Apply กล่าวอีกนัยหนึ่ง นอกจากเครื่องหมายต่อเนื่องแล้ว สแต็กการโทรก่อนและหลังการใช้ #%app ที่แก้ไขควรจะเทียบเท่ากัน - person soegaard; 23.11.2015

ไม่มีวิธีมาตรฐานในการทำ

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

คุณอาจได้รับสแต็กเทรซสั้นๆ เมื่อส่งสัญญาณถึงข้อผิดพลาดในเชิงลึก แต่ลักษณะที่ปรากฏนั้นเป็นการดำเนินการเฉพาะเจาะจง

ใน CL คุณสามารถติดตามฟังก์ชันได้ และคุณจะไม่เห็นเอาต์พุตใด ๆ จากการเรียก tail ต่อเนื่องกันเมื่อมีการปรับการเรียก tail ให้เหมาะสม

ภาษาที่ขี้เกียจไม่จำเป็นต้องเรียกซ้ำหางเนื่องจากการประเมินเป็นไปตามความจำเป็น

person Sylwester    schedule 18.06.2015