15
\$\begingroup\$

The "problem"

Define a function log (or other 3 letter name) which when called will log/print/write (whatever is the default for the language in question) both the instruction (as source) and the first argument. In other words:

i=777
j=333
log(i) //outputs: "log(i) 777"
log(i+j+1) //outputs: "log(i+j+1) 1111"

For all practical purposes the output i: 777 would be enough, but in some languages there are very specific reflection libraries for that, and that would be no challenge, so the entire instruction should be outputted.

Inspiration

The inspiration for this was me and another programmer discussing how irritating it is that often (with bad debuggers), you write things like console.log("i:", i), next we made a (pretty crazy) javascript (node only) solution (it outputs i: 777 rather than the entire line of source) which was suprisingly long and reminded me of codegolfing and made me wonder how much better other (especially code golfing) languages would fare.

Bonuses

-10%: No file reading (beyond the compiler)

PS. This is my first 'question' here, so feel free to point out any mistakes I made.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Welcome to CodeGolf.SE! I personally think your question is pretty decent, but it's usually a better idea to run question ideas through the sandbox to resolve ambiguities etc before people start working on answers. \$\endgroup\$ Mar 21, 2014 at 18:25
  • \$\begingroup\$ Thx and useful@sandbox, might be good to explain it's use on help/on-topic (it's mentioned, but I didn't consider it worth checking the way it was described there). \$\endgroup\$ Mar 21, 2014 at 18:36
  • \$\begingroup\$ @WolleVanillebärLutz: Of course it's not, did you see anyone claim that to be true then? \$\endgroup\$ Mar 27, 2014 at 20:45
  • \$\begingroup\$ The bounty is for TrungDQ (I think his solution is amazing just from a code perspective (better than our node only solution), regardless of length), have to wait 24 hours though before I can award it. \$\endgroup\$ Jun 8, 2014 at 15:53

13 Answers 13

14
\$\begingroup\$

C (40 -10% = 36) (38 -10% = 34.2)

Note that, in C, a log function can only be defined for a specific type. Therefore, this log "function" takes only int arguments.

#define log(x)printf("log("#x") %d",x)

A more general solution specifies how to print the argument, in addition to the argument itself:

#define lg2(f,x)printf("lg2("#x") "f,x)

which would be used as e.g. lg2("%s", "I'm a string"); or lg2("%f", 3.1415).

\$\endgroup\$
2
  • \$\begingroup\$ I don't think the final brackets around x are necessary. \$\endgroup\$
    – user12205
    Mar 21, 2014 at 19:07
  • \$\begingroup\$ @ace: I thought they might be needed if the user puts in some odd characters in the argument, but upon reflection I think you're right. I'll remove them. \$\endgroup\$
    – nneonneo
    Mar 21, 2014 at 19:09
10
\$\begingroup\$

Python (65 -10% = 58.5)

This assumes your code is in a file (it produces odd output if invoked in the interactive interpreter):

import traceback as t
def log(x):print t.extract_stack()[-2][3],x

It has been tested on Python 2.7.6.

Example:

def foo():
    x = 1
    log(x)
    for i in xrange(10):
        log(x+i+1)
    return x

log(foo())

outputs

log(x) 1
log(x+i+1) 2
log(x+i+1) 3
log(x+i+1) 4
log(x+i+1) 5
log(x+i+1) 6
log(x+i+1) 7
log(x+i+1) 8
log(x+i+1) 9
log(x+i+1) 10
log(x+i+1) 11
log(foo()) 1
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Nice! Got to say, this is the kind of crazy stuff that interests me as a programmer (negative index on a native function :O ) :P wonders off to find some docs \$\endgroup\$ Mar 21, 2014 at 19:16
9
\$\begingroup\$

C++ 121 71 67 -10% = 60.3

#include<iostream>
#define log(x)std::cout<<"log("#x") "<<(x)<<"\n"

Used like this:

int main() {
    int i = 777;
    int j = 333;
    log(i);
    log(i+j+1);
}

Outputs:

log(i) 777
log(i+j+1) 1111
\$\endgroup\$
3
  • \$\begingroup\$ You can remove 30 char and make a one-liner if you write it in C instead of C++: #define log(x)printf("log(%s) %d\n",#x,x), but that will only work of integers. \$\endgroup\$
    – user12205
    Mar 21, 2014 at 19:02
  • \$\begingroup\$ @ace: then it only works for one type. (Also, this is the solution I proposed, see below) \$\endgroup\$
    – nneonneo
    Mar 21, 2014 at 19:02
  • \$\begingroup\$ @nneonneo I hate it when I forgot to refresh before posting a comment. \$\endgroup\$
    – user12205
    Mar 21, 2014 at 19:05
5
\$\begingroup\$

Rebol3 - 31.5 (35 - 10 %)

Here is a simple implementation shortened from @draegtun that works well for numbers:

log: func[p][print[{log[}p{]}do p]]

Running it outputs:

>> log: func[p][print[{log[}p{]}do p]]
>> i: 777
>> j: 333
>> log [i]
log[ 777 ] 777
>> log[i + j + 1]
log[ i + j + 1 ] 1111

It can be much more flexible (for displaying the form of non-number types) at 42.3 chars (47 - 10%)

log: func[p][print[{log}mold p mold/only do p]]

The output:

>> log: func[p] [print[{log}mold p mold/only do p]]
>> log [join "4" 4]
log [join "4" 4] "44"  ;; shows a string
>> log [1 + 2]
log [1 + 2] 3 
\$\endgroup\$
4
+50
\$\begingroup\$

Javascript (325)

I think this is the log function you are looking for:

function log(m){L=(new Error()).stack.match(/(at log \([\s\S]+?at .+?:)\d+:\d+/m)[0].split('\n')[1].match(/:\d+:\d+/)[0];N=L.split(':')[1];C=parseInt(L.split(':')[2]);R=new XMLHttpRequest();R.open('GET',location.href,0);R.onload=function(){console.log(R.response.split('\n')[N-1].substr(C-1).split(';')[0]+' = '+m)};R.send()}

Usage

<script>
function log(m){L=(new Error()).stack.match(/(at log \([\s\S]+?at .+?:)\d+:\d+/m)[0].split('\n')[1].match(/:\d+:\d+/)[0];N=L.split(':')[1];C=parseInt(L.split(':')[2]);R=new XMLHttpRequest();R.open('GET',location.href,0);R.onload=function(){console.log(R.response.split('\n')[N-1].substr(C-1).split(';')[0]+' = '+m)};R.send()}

function doSomething() {
  var a = 123; log(a); var b = "Hello, I am TrungDQ!"; log(b);
}
doSomething();
var message = "...or just do it out here";
log(message + "!");
</script>

Output

log(a) = 123
log(b) = Hello, I am TrungDQ!
log(message + "!") = ...or just do it out here!

Long code

<script>
function log(msg) {
  // Get the line number and offset of the line where is function is called
  var lineInfo = (new Error()).stack.match(/(at log \([\s\S]+?at .+?:)\d+:\d+/m)[0].split('\n')[1].match(/:\d+:\d+/)[0];
  var lineNum = lineInfo.split(':')[1];
  var charOffset = parseInt(lineInfo.split(':')[2]);

  // Get the file source
  request = new XMLHttpRequest();
  request.open('GET', window.location.href, true);

  request.onload = function() {
    // Get file source code
    var response = request.responseText;
    // Get the `log` line
    var line = response.split('\n')[lineNum - 1];
    // Get the `log` statement
    var logStatement = line.substr(charOffset - 1).split(';')[0];
    // Print it
    console.log(logStatement + ' = ' + msg);
  };
  request.send();
}

function doSomething() {
  var a = 123; log(a); var b = "Hello, I am TrungDQ!"; log(b);
}
doSomething();
</script>

Only works when the script is put inside <script> tag which is put in .html document because it sends a request to location.href to get the source code. JSfiddle, F12 Dev Tool Console, embbed .js files won't work, I am trying to make it available everywhere...

Anyway, this question is interesting.

\$\endgroup\$
1
  • \$\begingroup\$ I'm little skeptical this is cross browsers. \$\endgroup\$ Mar 23, 2014 at 12:27
3
\$\begingroup\$

Scala - (221 - 10%) = 198.9

Yay macros! This is actually exactly the type of stuff they're for.

import language.experimental.macros
def log(p:Any)=macro l
def l(c:reflect.macros.Context)(p:c.Expr[Any])={import c.universe._;reify{println("log("+(c.Expr[String](Literal(Constant(show(p.tree)))).splice)+") "+p.splice)}}

Readable version:

import language.experimental.macros
def log(p: Any) = macro l
def l(c: reflect.macros.Context)(p: c.Expr[Any]) = {
  import c.universe._
  val inputString = show(p.tree)
  val inputStringExpr = c.Expr[String](Literal(Constant(inputString)))
  reify {
    println("log(" + (inputStringExpr.splice) + ") " + p.splice)
  }
}

Example:

log(1)
val x = 3
log(x)
val y = 4
log(x+y)

Outputs:

log(1) 1
log(x) 3
log(x.+(y)) 7

Since addition is a method call in Scala, it adds that verbose syntax back in, but it's pretty close! It's also a bit more verbose in a couple of other cases.

\$\endgroup\$
1
  • \$\begingroup\$ Wow, that's quite interesting to see@the addition of the function. So much cool stuff to learn :D \$\endgroup\$ Mar 21, 2014 at 23:46
2
\$\begingroup\$

bash (21 - 10% = 18.9)

This:

alias log=echo;set -v

Then use log like you would use echo:

log $((1+1))

or

A=2
B=3
log $((A+B))

This method will do all what is required; as a bonus, some extra information will also be printed, but no explicit rule forbid it.

\$\endgroup\$
2
\$\begingroup\$

BASH

Arguments are not passed using "(...)" in BASH, so I let the output of 'log()' fit that style:

$ log(){ echo "$FUNCNAME $@: $(($@))"; }
$ i=333
$ j=777
$ log i
log i: 333
$ log i+j+1
log i+j+1: 1111
\$\endgroup\$
1
  • \$\begingroup\$ $((...)) can be $[...] instead but I did not count the chars, so it doesn't matter up to now. \$\endgroup\$
    – user19214
    Jun 8, 2014 at 16:28
2
\$\begingroup\$

Clojure

(defmacro log[x] `(let [x# ~x] (println "log("'~x")" x#)))

Homoiconicity has its benefits!

To use:

(def i 777)
(def j 333)
(log i) ;Prints log( i ) 777
(log (+ i j 1)) ;Prints log( (+ i j 1) ) 1111

Let's see what's happening with macroexpand:

(macroexpand '(log (+ i j 1))) 
;; Prints the following: 
(let* [x__1__auto__ (+ i j 1)] (clojure.core/println "log(" (quote (+ i j 1)) ")" x__1__auto__))
\$\endgroup\$
1
  • \$\begingroup\$ If you quote x, do you really need to use an intermediate gensym (ie. x#)? I think you will only evaluate the expression once (btw, I am no Clojure expert) \$\endgroup\$
    – coredump
    Nov 2, 2014 at 14:40
2
\$\begingroup\$

Julia, 51*0.9=45.9

julia> x=4
4
julia> macro log(x) println("log($x) $(log(eval(x)))") end
julia> @log(x)
log(x) 1.3862943611198906

Alternatively, but not meeting the rules

julia> @show log(x)
log(x) => 1.3862943611198906
\$\endgroup\$
2
\$\begingroup\$

Tcl, 42.3 (47 - 10%)

proc log c {puts [dict g [info fr -1] cmd]\ $c}

Usage:

set i 777
set j 333
log $i  ;#outputs: "log $i 777"
log [expr {$i+$j+1}] ;#outputs: "log [expr {$i+$j+1}] 1111"

Edit: small improvement

\$\endgroup\$
0
\$\begingroup\$

Common Lisp - 119.7 (133 -10%)

(defmacro @(&whole f &rest r)(let((g(gensym)))`(let((,g(multiple-value-list,@r)))(progn(format t"~s~{ ~a~}
"',f,g)(values-list,g)))))
  • Named @ because log is the standard logarithm function and locked by default (at least on SBCL). Also, @ is only one character long.
  • Acts as a progn, taking a variable number of arguments, but prints to standard output. In real applications, I would probably signal a condition with an S-expression instead of printing space-separated output.
  • Contrary to the existing Clojure solution, we ultimately returns the value of the logged expression, so that (@ x) can be used whenever x is used.
  • Printing uses prin1, which outputs a read-able string. This is useful when trying to reproduce logged expressions.
  • Handles all possible types (see C answer)
  • Takes into account mutliple values
  • Does not produce different outputs (see Scala answer)
  • Works from a file and from REPL (See Pyhton answer)
  • Does not require browser/interpreter trick (Python traceback, Javascript request)

Sample outputs:

CL-USER>(@ (+ 3 2))   ; user input
(@ (+ 3 2)) 5         ; printed output
5                     ; result of expression

CL-USER> (@ (values 3 4))  ; input
(@ (VALUES 3 4)) 3 4       ; logging
3                          ; first value
4                          ; second value

CL-USER>(@ (round 3.4))
(@ (ROUND 3.4)) 3 0.4000001
3                          ; rounded value
0.4000001                  ; delta

And finally, if I log the above defmacro, I have the ungolfed version:

CL-USER> (@ (defmacro @(&whole f &rest r)(let((g(gensym)))`(let((,g(multiple-value-list,@r)))(progn(format t"~s~{ ~a~}
"',f,g)(values-list,g))))))
STYLE-WARNING: redefining COMMON-LISP-USER::@ in DEFMACRO
(@
 (DEFMACRO @ (&WHOLE F &REST R)
   (LET ((G (GENSYM)))
     `(LET ((,G (MULTIPLE-VALUE-LIST ,@R)))
        (PROGN
         (FORMAT T ,"~s~{ ~a~}
"
                 ',F ,G)
         (VALUES-LIST ,G)))))) @
@ ; actual result
\$\endgroup\$
0
\$\begingroup\$

PHP 138

You can't redeclare log in PHP without using another module (APD) so I used logg instead, i can resubmit with log example if needed. That's minor, but more sinful i guess is that this assume the log function is on a line by itself. I can update my answer as dictated by comments.

<?php function logg($v){$b=debug_backtrace()[0];$h=fopen($b['file'],"r");for($i=0;$i<$b['line']&&$l=fgets($h);$i++);echo trim($l)." $v";}

example output:

for ($i=1; $i<10; $i++) {   
  $j=$i+1;
  $k=$j+1;
  logg($i+$j+$k);
  echo "\n";
}
/*
logg($i+$j+$k); 6
logg($i+$j+$k); 9
logg($i+$j+$k); 12
logg($i+$j+$k); 15
logg($i+$j+$k); 18
logg($i+$j+$k); 21
logg($i+$j+$k); 24
logg($i+$j+$k); 27
logg($i+$j+$k); 30
*/
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.