Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

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.

share|improve this question
1  
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. –  Martin Büttner Mar 21 at 18:25
    
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). –  David Mulder Mar 21 at 18:36
    
@WolleVanillebärLutz: Of course it's not, did you see anyone claim that to be true then? –  David Mulder Mar 27 at 20:45
    
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. –  David Mulder Jun 8 at 15:53

14 Answers 14

up vote 13 down vote accepted

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).

share|improve this answer
    
I don't think the final brackets around x are necessary. –  ace Mar 21 at 19:07
    
@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. –  nneonneo Mar 21 at 19:09

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
share|improve this answer
    
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. –  ace Mar 21 at 19:02
    
@ace: then it only works for one type. (Also, this is the solution I proposed, see below) –  nneonneo Mar 21 at 19:02
    
@nneonneo I hate it when I forgot to refresh before posting a comment. –  ace Mar 21 at 19:05

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
share|improve this answer
1  
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 –  David Mulder Mar 21 at 19:16

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 
share|improve this answer

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.

share|improve this answer
    
I'm little skeptical this is cross browsers. –  Farid Nouri Neshat Mar 23 at 12:27

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.

share|improve this answer
    
Wow, that's quite interesting to see@the addition of the function. So much cool stuff to learn :D –  David Mulder Mar 21 at 23:46

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.

share|improve this answer

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
share|improve this answer
    
$((...)) can be $[...] instead but I did not count the chars, so it doesn't matter up to now. –  yeti Jun 8 at 16:28

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__))
share|improve this answer
    
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) –  coredump Nov 2 at 14:40

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
share|improve this answer

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

share|improve this answer

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
share|improve this answer

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
*/
share|improve this answer

JavaScript 55 53

function log(x){console.log('log("'+x+'") '+eval(x))}

Usage:

var i = 777,
    j = 333;
log("i")
log("j")
log("12*4")
log("i*j-4")

Output:

log("i") 777
log("j") 333
log("12*4") 48
log("i*j-4") 258737

You MUST use double quotes " otherwise it will not work.

share|improve this answer
    
Regardless of it already bending the rules by not following the example pseudo code I provided, the bigger problem is that it only works if and only if the variables are defined in the global context (I know the eval evaluation context is more complex than that, but point stands) –  David Mulder Mar 21 at 19:06
    
The point of the challenge was that you don't pass a string... -1 –  Doorknob 冰 Mar 21 at 20:31
    
The point was not doing log("i:", i) ... I don't think it can't be done without ' or " in js ... you can make it smaller using console.log('log('+o+')'+eval(x)) but the output won't match the code line (who cares) –  rafaelcastrocouto Mar 21 at 22:34
2  
You can do it in a single line, I did it in node, how? By throwing an error, getting the stack, reading the file and extracting the line. Yep, kinda crazy :D. Additionally it might be possible using arguments.callee.caller.toString(), but I wasn't able to figure out which line is which when you have two logs. –  David Mulder Mar 21 at 23:34

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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