Odd word problem: Difference between revisions
(Add Factor, but this first version is {{incorrect}}.) |
|||
Line 14: | Line 14: | ||
'''Test case''': work on both the "life" example given above, and the text <code>we,are;not,in,kansas;any,more.</code> |
'''Test case''': work on both the "life" example given above, and the text <code>we,are;not,in,kansas;any,more.</code> |
||
=={{header|Factor}}== |
|||
{{incorrect|Factor|The final "." is missing from the output.}} |
|||
This is delicate code with arcane control flow. |
|||
<lang factor>USING: continuations kernel io io.streams.string locals unicode.categories ; |
|||
FROM: sets => in? ; |
|||
IN: rosetta.odd-word |
|||
<PRIVATE |
|||
! Save current continuation. |
|||
: savecc ( -- continuation/f ) |
|||
[ ] callcc1 ; inline |
|||
: jump-back ( continuation -- ) |
|||
f swap continue-with ; inline |
|||
PRIVATE> |
|||
:: odd-word ( -- ) |
|||
f :> first-continuation! |
|||
f :> last-continuation! |
|||
f :> reverse! |
|||
! Read characters. Loop until reading "." or end of stream. |
|||
[ read1 dup { CHAR: . f } in? ] [ |
|||
dup Letter? [ |
|||
! This character is a letter. |
|||
reverse [ |
|||
last-continuation savecc |
|||
dup [ |
|||
last-continuation! |
|||
2drop ! Drop letter and previous continuation. |
|||
] [ |
|||
drop |
|||
swap write1 |
|||
jump-back |
|||
] if |
|||
] [ write1 ] if |
|||
] [ |
|||
! This character is not a letter. Assume punctuation. |
|||
reverse [ |
|||
savecc dup [ |
|||
first-continuation! |
|||
last-continuation jump-back |
|||
] [ drop ] if |
|||
write1 |
|||
f reverse! |
|||
] [ |
|||
write1 |
|||
t reverse! |
|||
savecc dup [ |
|||
last-continuation! |
|||
] [ first-continuation jump-back ] if |
|||
] if |
|||
] if |
|||
] until |
|||
drop ! Drop "." or f. |
|||
reverse [ |
|||
savecc dup [ |
|||
first-continuation! |
|||
last-continuation jump-back |
|||
] [ drop ] if |
|||
] when |
|||
nl ! Print a cosmetic newline. |
|||
; |
|||
: string-odd-word ( string -- ) |
|||
[ odd-word ] with-string-reader ;</lang> |
|||
'''USE: rosetta.odd-word''' |
|||
( scratchpad ) '''"what,is,the;meaning,of:life." string-odd-word''' |
|||
what,si,the;gninaem,of:efil |
|||
( scratchpad ) '''"we,are;not,in,kansas;any,more." string-odd-word''' |
|||
we,era;not,ni,kansas;yna,more |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
<lang go>package main |
<lang go>package main |
Revision as of 03:55, 5 November 2011
Write a program that solves the odd word problem.
Description: You are promised an input stream consisting of English letters and punctuations. It is guaranteed that
- the words (sequence of consecutive letters) are delimited by one and only one punctuation; that
- the stream will begin with a word; that
- the words will be at least one letter long; and that
- a full stop (.) appears after, and only after, the last word.
For example, what,is,the;meaning,of:life.
is such a stream with six words. Your task is to reverse the letters in every other word while leaving punctuations intact, producing e.g. "what,si,the;gninaem,of:efil.", while observing the following restrictions:
- Only I/O allowed is reading or writing one character at a time, which means: no reading in a string, no peeking ahead, no pushing characters back into the stream, and no storing characters in a global variable for later use;
- You are not to explicitly save characters in a collection data structure, such as arrays, strings, hash tables, etc, for later reversal;
- You are allowed to use recursions, closures, continuations, threads, coroutines, etc.
Test case: work on both the "life" example given above, and the text we,are;not,in,kansas;any,more.
Factor
This is delicate code with arcane control flow.
<lang factor>USING: continuations kernel io io.streams.string locals unicode.categories ; FROM: sets => in? ; IN: rosetta.odd-word
<PRIVATE ! Save current continuation.
- savecc ( -- continuation/f )
[ ] callcc1 ; inline
- jump-back ( continuation -- )
f swap continue-with ; inline
PRIVATE>
- odd-word ( -- )
f :> first-continuation! f :> last-continuation! f :> reverse! ! Read characters. Loop until reading "." or end of stream. [ read1 dup { CHAR: . f } in? ] [ dup Letter? [ ! This character is a letter. reverse [ last-continuation savecc dup [ last-continuation! 2drop ! Drop letter and previous continuation. ] [ drop swap write1 jump-back ] if ] [ write1 ] if ] [ ! This character is not a letter. Assume punctuation. reverse [ savecc dup [ first-continuation! last-continuation jump-back ] [ drop ] if write1 f reverse! ] [ write1 t reverse! savecc dup [ last-continuation! ] [ first-continuation jump-back ] if ] if ] if ] until drop ! Drop "." or f. reverse [ savecc dup [ first-continuation! last-continuation jump-back ] [ drop ] if ] when nl ! Print a cosmetic newline. ;
- string-odd-word ( string -- )
[ odd-word ] with-string-reader ;</lang>
USE: rosetta.odd-word ( scratchpad ) "what,is,the;meaning,of:life." string-odd-word what,si,the;gninaem,of:efil ( scratchpad ) "we,are;not,in,kansas;any,more." string-odd-word we,era;not,ni,kansas;yna,more
Go
<lang go>package main
import (
"bytes" "fmt" "io" "os" "unicode"
)
func main() {
owp(os.Stdout, bytes.NewBufferString("what,is,the;meaning,of:life.")) fmt.Println() owp(os.Stdout, bytes.NewBufferString("we,are;not,in,kansas;any,more.")) fmt.Println()
}
func owp(dst io.Writer, src io.Reader) {
byte_in := func () byte { bs := make([]byte, 1) src.Read(bs) return bs[0] } byte_out := func (b byte) { dst.Write([]byte{b}) } var odd func() byte odd = func() byte { s := byte_in() if unicode.IsPunct(rune(s)) { return s } b := odd() byte_out(s) return b } for { for { b := byte_in() byte_out(b) if b == '.' { return } if unicode.IsPunct(rune(b)) { break } } b := odd() byte_out(b) if b == '.' { return } }
}</lang> Output:
what,si,the;gninaem,of:efil. we,era;not,ni,kansas;yna,more.
A different approach, using defer
:
<lang go>package main
import (
"bytes" "fmt" "io" "os" "unicode"
)
func main() {
owp(os.Stdout, bytes.NewBufferString("what,is,the;meaning,of:life.")) fmt.Println() owp(os.Stdout, bytes.NewBufferString("we,are;not,in,kansas;any,more.")) fmt.Println()
}
func owp(dst io.Writer, src io.Reader) {
byte_in := func () byte { bs := make([]byte, 1) src.Read(bs) return bs[0] } byte_out := func (b byte) { dst.Write([]byte{b}) } odd := func() byte { for { b := byte_in() if unicode.IsPunct(int(b)) { return b } defer byte_out(b) } panic("impossible") } for { for { b := byte_in() byte_out(b) if b == '.' { return } if unicode.IsPunct(rune(b)) { break } } b := odd() byte_out(b) if b == '.' { return } }
}</lang>
Icon and Unicon
The following recursive version is based on the non-deferred GO version. A co-expression is used to turn the parameter to the wrapper into a character at a time stream.
<lang Icon>procedure main() every OddWord(!["what,is,the;meaning,of:life.",
"we,are;not,in,kansas;any,more."])
end
procedure OddWord(stream) # wrapper for demonstration
write("Input stream: ",stream) writes("Output stream: ") & even(create !stream,'.,;:') & write()
end
procedure odd(stream,marks)
if any(marks,s := @stream) then return s return 1(odd(stream,marks), writes(s))
end
procedure even(stream,marks)
repeat { repeat writes(@stream) ? if ="." then return else if any(marks) then break if writes(odd(stream,marks)) == '.' then return }
end</lang>
Output:
Input stream: what,is,the;meaning,of:life. Output stream: what,si,the;gninaem,of:efil. Input stream: we,are;not,in,kansas;any,more. Output stream: we,era;not,ni,kansas;yna,more.
Java
This is translated from the first C version on the solutions page. <lang java>import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader;
public class OddWord { public static void processStream(BufferedReader in) throws IOException{ if(checkEnd(in))return; while(true){ forward(in); if(checkEnd(in))return; reverse(in); if(checkEnd(in))return; } }
private static boolean checkEnd(BufferedReader in) throws IOException{ if(peek(in) == '.'){ System.out.println((char)in.read()); return true; }else{ System.out.print((char)in.read()); return false; } }
private static char peek(BufferedReader in) throws IOException{ in.mark(1); char retVal = (char)in.read(); in.reset(); return retVal; }
private static void forward(BufferedReader in) throws IOException{ while(Character.isLetter(peek(in))){ System.out.print((char)in.read()); } }
private static void reverse(BufferedReader in) throws IOException{ if(Character.isLetter(peek(in))){ char character = (char)in.read(); reverse(in); System.out.print(character); } }
public static void main(String[] args) throws IOException{ processStream(new BufferedReader(new StringReader("what,is,the;meaning,of:life."))); processStream(new BufferedReader(new StringReader("we,are;not,in,kansas;any,more."))); processStream(new BufferedReader(new StringReader(";what,is,the;meaning,of:life."))); processStream(new BufferedReader(new StringReader("'we,are;not,in,kansas;any,more."))); } }</lang> Output:
what,si,the;gninaem,of:efil. we,era;not,ni,kansas;yna,more. ;what,si,the;gninaem,of:efil. 'we,era;not,ni,kansas;yna,more.
Python
<lang python>from sys import stdin, stdout
def char_in(): return stdin.read(1) def char_out(c): stdout.write(c)
def odd(prev = lambda: None): a = char_in() if not a.isalpha(): prev() char_out(a) return a != '.'
# delay action until later, in the shape of a closure def clos(): char_out(a) prev()
return odd(clos)
def even(): while True: c = char_in() char_out(c) if not c.isalpha(): return c != '.'
e = False while odd() if e else even(): e = not e</lang> Running:<lang>$ echo "what,is,the;meaning,of:life." | python odd.py what,si,the;gninaem,of:efil. $ echo "we,are;not,in,kansas;any,more." | python odd.py we,era;not,ni,kansas;yna,more.</lang>
Scheme
Output is identical to python. <lang lisp>(define (odd)
(let ((c (read-char))) (if (char-alphabetic? c) (let ((r (odd)))
(write-char c) r)
(lambda () (write-char c) c))))
(define (even)
(let ((c (read-char))) (write-char c) (if (char-alphabetic? c) (even) c)))
(let loop ((i #f))
(let ((c (if i ((odd)) (even)))) (if (char=? c #\.) (exit) (loop (not i)))))</lang>
Tcl
Although the input is handled as strings, they're all as single-character strings.
<lang tcl>package require Tcl 8.6
proc fwd c {
expr {[string is alpha $c] ? "[fwd [yield f][puts -nonewline $c]]" : $c}
} proc rev c {
expr {[string is alpha $c] ? "[rev [yield r]][puts -nonewline $c]" : $c}
} coroutine f while 1 {puts -nonewline [fwd [yield r]]} coroutine r while 1 {puts -nonewline [rev [yield f]]} for {set coro f} {![eof stdin]} {} {
set coro [$coro [read stdin 1]]
}</lang> Output is identical to Python and Scheme versions.
The only difference between the two coroutines (apart from the different names used when flipping back and forth) is the timing of the write of the character with respect to the recursive call.