4

As an attempt to learn more about call/cc in general I saw How does the yin-yang puzzle work? question and this explaination about the yin-yang puzzle:

(let*
    (
        (yin (
            (lambda (cc) (display #\@) cc)
            (call/cc (lambda (c) c))
        ))
        (yang (
            (lambda (cc) (display #\*) cc)
            (call/cc (lambda (c) c))
        ))
    )
    (yin yang)
)

I probably (or probably not) understood the yin-yang puzzle concept as of by now. But I found that scheme syntax is not particularly easy to understand, and upon searching I found that ruby have Continuation module. Since ruby syntax follows procedural style, I found that it's much easier to read ruby code than scheme code. Therefore I decided to translate the puzzle to ruby version (I'm both novice at scheme and ruby):

require "continuation"

yin = (lambda do |cc|
    print "@"
    return cc
end).call(callcc {|c| c})

yang = (lambda do |cc|
    print "*"
    return cc
end).call(callcc {|c| c})

yin.call(yang)

However this version prints out @*@***********... (demo here) instead of @*@**@***@****@*****@**..., which is not what I expected.

Is my ruby version correct? If it's not correct then I don't really know what's to do from here...

Trung0246
  • 689
  • 1
  • 10
  • 21
  • "_...I found that it's much easier to read ruby code than scheme code._" -- Well, it is much harder to read lisp code when it is formatted in the manner of a curly braces language. Don't scatter parentheses all over different lines; study the formatting of lisp code written idiomatically, and emulate that. Once you get used to the way lisp is typically formatted, it is about as easy and natural to read as any other code. – ad absurdum Aug 05 '21 at 05:16

1 Answers1

0

i think your problem that yin and yang will become Continuation and they don't call together but only yang nested inside yin.

yin = (lambda do |cc|
  print "@"
  return cc # (3) return Continuation (1)
end).call(callcc {|c| c}) # (1) this params call first

# (2) lambda will call immediately -> print the first @
# so yin = Continuation (1)

yang = (lambda do |cc|
  print "*"
  return cc # (6) return Continuation (4)
end).call(callcc {|c| c}) # (4) this params call first *

# (5) lambda will call immediately -> print the first *
# so yang = Continuation (4)

yin.call(yang)

now we could interpret your code as below

yin = callcc {|c| c} # yin context
  print '@'
  yang = callcc {|cc| cc} # yang context
    print '*'
    yin.call(yang)
  # end yang context
# end yin context

as you can see, after the first time call (the second @ and *) the last line yin.call(yang) will keep call yang yang ( * yang ( * yang ( ..., if you replace by yin.call(yin), the output will be @*@*@*@*...

finally, here is my solution, the idea is that yin and yang will be nested together, the next yang will contains the prev yin + '*' and the next yin will contains the previous yang + '@'

require "continuation"

yin = lambda { |yang|
  cc = callcc { |cc| cc }
  print "@"
  yang.call(cc)
}
  
yang = lambda { |yin|
  cc = callcc { |cc| cc }
  print "*"
  yin.call(cc)
}

yin.call(yang)
Lam Phan
  • 3,405
  • 2
  • 9
  • 20