Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Closest-value-in-list Golf!

by larryk (Friar)
on May 29, 2001 at 17:47 UTC ( #83923=perlmeditation: print w/replies, xml ) Need Help??

Inspired by neophyte's question in the chatter about parsing a list of numerical data for the nearest value to a given (number) value, I tee off timidly and prepare for an ass-whoopin'.

1. sub must take 2 params:
  1) list (or listref) of _numerical_ data (minimum 1 element)
  2) a number - int/real/negative - whatever! as the "match-closest-to-this" number
not necessarily in that order.
2. the original order of the data must be maintained - you can't sort the data directly.
3. can't think of any more!

Update: 19:45 29/05/2001 (GMT)
Apologies if I did not make this clear: 2 things go into the sub - a number and a list. the order in which you pass these is up to you. the list can either go in as a strtaight list or as a reference to a list - your choice - just so long as the original isn't modified. and the return value from the sub should be either the value closest to the given number or the index in the original list.


sub g {blah} print "Closest value is ",g(@list,$number); # or print "Closest value is ",g($number,@list); # or print "Closest value is ",g(\@list,$number); # or print "Closest value is ",g($number,\@list); # OR print "Closest value is ",$list[g(@list,$number)]; # or print "Closest value is ",$list[g($number,@list)]; # or print "Closest value is ",$list[g(\@list,$number)]; # or print "Closest value is ",$list[g($number,\@list)];

#!perl -w use strict; chomp(my @list = <DATA>); # FORE!!! 78 chars. sub g {my%h=();my$n=pop;$h{abs($_-$n)}=$_ for@_;my@s=sort{$a<=>$b}keys +%h;$h{shift@s};} print g(@list,10); __DATA__ 17 1.4 18.2 7.9 12.2 12.5 1.1 7.8 18.3 20 6.7 6.9 18.1 1.5 17.7 16.6 1.2 1.3 17.5
I can't believe I had to leave a space in!

"Argument is futile - you will be ignorralated!"

Replies are listed 'Best First'.
Re (tilly) 1: Closest-value-in-list Golf!
by tilly (Archbishop) on May 29, 2001 at 18:40 UTC
    First of all I don't think that strict is a very useful restriction in golf. For instance a useful trick is to use punctuation variables like $, because Perl parses $,for differently than $_for. But using $, is strict-compliant in letter even though it definitely is not in spirit!

    That said the natural approach at 46 letters happens to be strict-compliant:

    sub g{ (sort{abs($a-$_[1])<=>abs$b-$_[1]}@{$_[0]})[0] }
      how come you use ( ) on one abs but not the other? :-)
      sub g {(sort{abs$a-$_[1]<=>abs$b-$_[1]}@{$_[0]})[0]} # 44 chars
      pop and no ref...
      sub g {$n=pop;(sort{abs$a-$n<=>abs$b-$n}@_)[0]} # 39 chars
      fantastic - that's exactly half my original

      "Argument is futile - you will be ignorralated!"

        For future reference, I found your spec unclear. In the end I settled on "2 parameters" as being the clearest statement. But it would help in the future if you gave sample test code using the function. Then there can be no doubt.

        However if I am allowed to rearrange the arguments, I find it more natural that any special arguments should come first in the list. Giving a 38 character solution:

        sub g { (sort{abs$a-$_[0]<=>abs$b-$_[0]}@_)[1] }
      Update my bad... sorry, missed the or listref bit of golf, my response here was useless
                      - Ant
Re: Closest-value-in-list Golf!
by chipmunk (Parson) on May 29, 2001 at 20:26 UTC
    I disagree with tilly that using sort() is the natural solution. I think this is the natural solution:
    sub closest { $n=pop;$c=pop;abs$n-$_<abs$n-$c?$c=$_:0for@_;$c }
    This is O(n) rather than O(n*log n). Of course, at 47 characters it is, unfortunately, a longer solution. Oh well!
Re: Closest-value-in-list Golf!
by suaveant (Parson) on May 29, 2001 at 17:57 UTC
    Why are you using strict in golf if it is not required?
    sub g {$n=pop;$h{abs($_-$n)}=$_ for@_;@s=sort{$a<=>$b}keys%h;$h{shift@ +s};}
    66 chars with strict off


    sub g {$n=pop;$h{abs($_-$n)}=$_ for@_;@s=sort{$a<=>$b}keys%h;$h{shift@ +s}}
    oops trailing ; is extraneous... 65 strokes...

    Update2 oooh!

    sub g {$n=pop;$h{abs($_-$n)}=$_ for@_;$h{(sort{$a<=>$b}keys%h)[0]}}
    59 strokes
                    - Ant
      is too much, since $a<=>$b is the default sort. that leaves you with
      sub g {$n=pop;$h{abs($_-$n)}=$_ for@_;$h{(sort keys%h)[0]}}
      for 52.
        did you test this? i think you'll find that $a cmp $b is the default.

        "Argument is futile - you will be ignorralated!"

        Test your code...

        that does not work... I tried it already... returns 20
                        - Ant

Re: Closest-value-in-list Golf!
by jeroenes (Priest) on May 29, 2001 at 19:12 UTC
    I can offer a 47 36 char solution with the use of PDL:
    sub i{my$a=pdl$_[0];minimum_ind(abs($a-$_[1]))}
    and it is strict-compliant and very straightforward.

    Of course it returns the index, but that is allowed, isn't it? Otherwise, add 4 chars to get the value.

    Hey wait, do you only count the chars between {}? Than we can take of 7, that counts 40 chars. And there is room in the last part:

    sub i{my$a=pdl$_[0];minimum_ind abs$a-pop}
    And we are at 36 chars.

    "We are not alone"(FZ)

      Why do you not count "use PDL"?
        Should have mentioned that. Add 7 chars.

        Use of modules actually should disqualify a golf, methinks. It's just that I've come to like matrix calc, and couldn't resist such a solution.

(boo) Re: Closest-value-in-list Golf!
by boo_radley (Parson) on May 29, 2001 at 17:58 UTC
    oops :
    Warning: Use of "keys" without parens is ambiguous at C:\ line 8.
    my answer is forthcoming, will update.
    update you can shave off a few chars by changing
    . Don't get too complex, eh? UpdateTime's up!
    sub e {sub n{abs($s-$b)};$s=pop;while($b=pop){if(&n<$d||!$d){$l=$b;$d= +&n}}$l}
    for a total of 70 chars.
      even shorter to not assign to @s at all, like my Update2
                      - Ant
        sub g {$n=pop;$h{abs$_-$n}=$_ for@_;$h{(sort{$a<=>$b}keys%h)[0]}}

        57 if you take out the ( ) on the abs

        "Argument is futile - you will be ignorralated!"

Re: Closest-value-in-list Golf!
by Masem (Monsignor) on May 29, 2001 at 18:41 UTC
    Here's a 62 char solution, imploying half a schwatzian transform:
    sub g{ #234567890123456789012345678901234567890123456789012345678901234567890 my$c=pop;(sort{@$a[1]<=>@$b[1]}map{[$_,abs($_-$c)]}@_)[0]->[0] }

    Dr. Michael K. Neylon - || "You've left the lens cap of your mind on again, Pinky" - The Brain
Re: Closest-value-in-list Golf!
by ChOas (Curate) on May 29, 2001 at 18:13 UTC

    I think it`s shorter, and I got no spaces...

    But I don`t know how to count...

    sub g{%_=map{abs($_[-1]-$_[$_]),$_}(0..@_-2);$_[$_{(sort{$a<=>$b}keys% +_)[0]}]}


    print "profeth still\n" if /bird|devil/;
      count between the {}s of the sub - yours is 71

      "Argument is futile - you will be ignorralated!"

Re: Closest-value-in-list Golf!
by ChOas (Curate) on May 29, 2001 at 18:46 UTC
    55 ... and now I give up....

    sub g{%_=map{abs$_[-1]-$_,$_}@_;$_{(sort{$a<=>$b}keys%_)[1]}}


    print "profeth still\n" if /bird|devil/;
Re: Closest-value-in-list Golf!
by mr_mischief (Monsignor) on May 31, 2001 at 06:55 UTC
    Not the shortest, but this was what was obvious to me...
    sub c { #_123456789_123456789_123456789_123456789_12345 $i=pop;for(@_){$c=$_ if abs$i-$c>abs$i-$_}$c }
Re: Closest-value-in-list Golf!
by mr.nick (Chaplain) on May 30, 2001 at 22:47 UTC
    Hm. I used a different approach, and if my eyes don't deceive me, it's the lowest score yet. Update: No it's not ... damn:
    sub g { # 1 2 3 4 5 # 1234567890123456789012345678901234567890123456789012 $n=pop;$,=pop;$,=abs$n-$_<abs$n-$,?$_:$,for@_;$, }
    for a score of 48.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://83923]
Approved by root
choroba had a workshop with the band
[choroba]: which counts as a good weekend
[Discipulus]: yes, (at least until Sun afternoon...): Saturday we got splendid birthday party in a park: lot of eat, drink and children amusement: bag running, magnetic fishing, rope and that big pot full of candies to smash with a club

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (5)
As of 2017-09-25 08:40 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (278 votes). Check out past polls.