I recently started using Racket and i couldn't find a good alternative to C's scanf/printf in the racket standard library. When doing programming challenges, you often need to comply to a specific input/ouput thus having something like scanf for string to values and printf for values to string is comfy.
For example, let's say a challenge where you simply have to output the input but you need to translate the strings input into values and then those values back to strings.
The given input(and excepted ouput) is
3
[04, foo, 03.5]
[05, bar, 04.6]
[06, fun, 05.7]
In C, you would simply do
#include <stdio.h>
int main(void)
{
int n;
// Read number of rows
scanf("%d\n", &n);
// Output number of rows
printf("%d\n", n);
// Process rows
for (unsigned int i; i < n; ++i)
{
int a;
char b[20];
float c;
// Read row
scanf("[%d, %[^,], %f]\n", &a, b, &c);
// Output row
printf("[%02d, %s, %04.1f]\n", a, b, c);
}
}
Now, for a solution in Racket, You first have to read the first line and convert it into a number.
(define rows (string->number (read-line)))
Then, for all rows, read and split the string. The best that i have found to do that is using regular expressions.
(define split-row (regexp-match #rx"\\[(.+), (.+), (.+)\\]" (read-line)))
Then you have to manually convert the substrings into values
(define a (string->number (second split-row)))
(define b (third split-row))
(define c (string->number (fourth split-row)))
Then you have to manually convert the values back into strings
(printf "[~a, ~a, ~a]\n"
(~a a #:width 2 #:align 'right #:pad-string "0")
b
(~r c #:min-width 4 #:precision 1 #:pad-string "0")))
This is way more tedious than with the classical input/output format, especially when you are doing coding challenges.
Final Racket solution:
#lang racket
(define rows (string->number (read-line)))
(for ([in-range rows])
(define split-row (regexp-match #rx"\\[(.+), (.+), (.+)\\]" (read-line)))
(define a (string->number (second split-row)))
(define b (third split-row))
(define c (string->number (fourth split-row)))
(printf "[~a, ~a, ~a]\n"
(~a a #:width 2 #:align 'right #:pad-string "0")
b
(~r c #:min-width 4 #:precision 1 #:pad-string "0")))
Having something like (not necessary the same specifiers as with printf)
(string-scan "[%d, %s, %f]" "[45, foo, 10.9]") -> '(45 "foo" 10.9)
and a proper output formatter would be comfy.
Are racket devs against such a thing in the standard library ?
How you guys are actually doing IO in racket ?
In Racket, (read) and (write) know all the builtin datatypes which are already structured more than a stream of bytes (like in C).Thus, you don't need scanf to tell Racket what is the type of the next token.That *is* painful in a situation like coding challenges since the input format is language independent (however actually it's the C style).Of course this kind of situation also has its own fixed format, you can define your own read tables :1. the "," is special in Racket, you need to drop off them first; (with-input-from-string "[04 foo 03.5]" read) gives you '(4 foo 3.5) directly.2. Symbols are internal Strings, you need (symbol->string) to covent them into normal Strings (Yes, sometimes, I think if there are string-like or bytes-like APIs that work on symbols directly).
:) I had started writing up a parsack example, and I was all set to
admonish the OP for not creating a parser when you want a parser but
then I saw it was for a programming contest where I guess this sort of
scanf/regexp hackery is ok?
About using an external package, there also the case like on www.hackerrank.com where you have to run the code in their own environment (eg: http://i.imgur.com/iSSPLGy.png).
I use racket for hackerrank and coding contests all the time, and I find it's read syntax really useful. For instance, I would parse this into a list of lists by doing
(for/list ([i (in-range (read))])
(map (lambda (x) (if (list? x) (cadr x) x)) (read))).
Then, to print out results, I normally do
(define (main n)
(unless (= 0 n)
(begin (printf "~a ... ~a\n", args...)
(main (sub1 n)))).
Another really useful helper is
(define (read->list n)
(if (= 0 n) '()
(cons (read) (read->list (sub1 n)))).
You can customize these by doing for/vector, for/fold, etc., and there hasn't been a hackerrank contest I've run into that I haven't been able to do with some technique like this.
Correction, not "read syntax". My PL professor would fail me for that. I'd say that there are very few hackerrank and other contests input format that can't be parsed as an s-expression in some way (unless they do weird things with parentheses, in which case you have to use read-line/char/byte).
Just note that a value with a comma in front of it is parsed as a list where the first item in the list is the unquote, and the second item is the value itself. Regardless, it's fairly easy to use the common list functions to extract the data you want.
doc: http://people.csail.mit.edu/jaffer/slib/Standard-Formatted-Input.html#Standard-Formatted-Input
code: http://cvs.savannah.gnu.org/viewvc/slib/slib/scanf.scm?view=markup
[1] https://en.wikipedia.org/wiki/SLIB
[2] http://people.csail.mit.edu/jaffer/SLIB.html
[3] http://blog.racket-lang.org/2007/11/getting-rid-of-set-car-and-set-cdr.html
Right, that's the library that I borrowed for the code I posted. Fortunately the code didn't need to mutate pairs so it seems to work.
Sam
--
You received this message because you are subscribed to the Google Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Luckily, major programming competitions (say, Facebook Hacker Cup, Google Code Jam, early IOI, ACM-ICPC, etc.) usually have simple input format. Data are usually separated by spaces. Rarely spaces matter. rom gcb says that the input is:
3
[04, foo, 03.5]
[05, bar, 04.6]
[06, fun, 05.7]
but in these competitions, the input is more likely to be:
3
04 foo 03.5
05 bar 04.6
06 fun 05.7
What really gets in the way is that strings in the input could be something that Racket's (read) doesn't recognize. As an example, a string might be #asd. This would cause (read) to error "read: bad syntax `#a'".
Indeed, using (read-line) along with regex would solve the problem. The downside is that it requires a lot of writing and is heavily task-dependent. What I want is a uniform way to extract data easily like scanf.
I write the following module for this purpose. It's a lot of code, so it would be useless in closed competitions like ACM-ICPC. However, for online competitions like FB Hacker Cup, I find it pretty useful.
#lang racket
(provide (all-defined-out))
(define read-next
(let ([buffer #f])
(lambda (#:to-eol [to-eol #f])
(define (more!) (set! buffer (read-line)) (read-next #:to-eol to-eol))
(match buffer
[(? string?)
(cond
[to-eol (let ([ret buffer]) (set! buffer #f) ret)]
[else
(set! buffer (string-trim buffer #:right? #f))
(match (regexp-match #px"\\S+" buffer)
[(list ret) (set! buffer (regexp-replace #px"^\\S+" buffer "")) ret]
[#f (more!)])])]
[_ (more!)]))))
(define (%list n typ) (thunk (for/list ([_ n]) (typ))))
(define (%gets) (read-next #:to-eol #t))
(define (%num) (string->number (read-next)))
(define (%str) (read-next))
(define-syntax-rule (scan [v m] ...) (begin (define v (m)) ...))
(module+ test
(require rackunit)
(parameterize
([current-input-port
(open-input-string
(string-append " 2 3 \n"
"1 2 3\n"
" 4 5 6 \n"
" \n"
" \n"
" \n"
"\n"
" '\"has-quotes\"' blah blah "))])
(scan [n %num]
[m %num]
[matrix (%list n (%list m %num))]
[trailing-spaces %gets]
[str-with-quotes %str]
[line-with-space %gets])
(check-equal? n 2)
(check-equal? m 3)
(check-equal? matrix (list (list 1 2 3) (list 4 5 6)))
(check-equal? trailing-spaces " ")
(check-equal? str-with-quotes "'\"has-quotes\"'")
(check-equal? line-with-space " blah blah ")))
For example, https://code.google.com/codejam/contest/6254486/dashboard#s=p1 can be solved as follows:
(scan [n %num])
(for ([i n])
(scan [s %str])
(define simp (regexp-replaces s '([#px"\\++" "+"] [#px"\\-+" "-"])))
(define len (string-length simp))
(printf "Case #~a: ~a\n"
(add1 i)
(match (list (string-ref simp 0) (even? len))
[(or (list #\+ #t) (list #\- #f)) len]
[(or (list #\+ #f) (list #\- #t)) (sub1 len)])))
> email to racket-users+unsubscribe@googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscribe@googlegroups.com.
On Jul 22, 2017, at 12:30 PM, David Storrs <david....@gmail.com> wrote:
One thing that would solve a lot of this issue would be if the pregexp syntax added support for named captures as in Perl and the PCRE library that exports them.
Alternatively, if 'match' made the results of a successful regexp test available to the bodies on the RHS then you could do the same thing by accessing the result list. Perhaps if match would allow the RHS to be a function?