HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Find the number closest to 0, breaking ties in favor of positive numbers

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
findnumberthetiesfavornumbersbreakingclosestpositive

Problem

I'm a Perl beginner, I really love this language, and I was wondering if it was possible to reduce the size of this code, just for fun and so as to learn Perl specificities:

chomp($n=);
if (!$n) {
   print 0;   
} else {
   @t=split/ /,;
   foreach $i(0..$n-1) {
      $m=$t[$i]if(!$m)||(abs($t[$i])0));    
   }
}print $m;


The goal of this code is to give the closest number to 0 of a list, and if there are -4 and 4 for example, it will always return the positive number.

-
$n is the number of numbers in the list

-
@t is the list as an array

-
$m is the min

This is "Temperatures" game on the website Codingame.com.

This code is working pretty well, and I'm trying to reduce its size so as to discover some Perl secrets.

I have some precise questions:

  • Is it possible to use the ternary operator with several instructions in Perl?



  • Is is possible to write the foreach loop on one line in this case, or even replace the foreach loop with something else?



I don't really want perfect code as an answer, but some indications, and then I can search details and learn by myself.

Solution

A bad example

Perl is a language in which the shortest code is usually not the best code. For example, this short program accomplishes the task:

print((sort {abs($a)abs($b) || $b$a} (split ' ',<>)[0..<>-1])[0] // 0)


Not only is it hard to read, it's also algorithmically inefficient (because sorting is O(n log n) rather than O(n)). I don't recommend writing code like that.

Critique

You should use more whitespace. It won't make your code look any less clever, nor will it make your code run any slower.

You can safely dispense with chomp(), as Perl will naturally disregard the trailing newline when interpreting the string as a number. You could probably write <> instead of `, unless you insist on reading only from standard input.

Counting loops are clumsy in Perl. When possible, opt for higher-level list-processing techniques. In this case, I recommend
List::Util::reduce.

A possible solution

In Perl, there are usually many possible ways to solve a problem. Here's one short solution that is O(n), but avoids being the shortest possible solution. I'll let you figure out how it takes advantage of the
operator.

use List::Util qw(max reduce);
use strict;

my $n = <>;
my @temps = (split ' ', <>)[0 .. $n - 1];
print((reduce { ($a, max($a, $b), $b)[1 + (abs($a)  abs($b))] } @temps)
       // 0);


The special case of
$n == 0 can be handled using a // logical defined-or operator. A || logical or operator would also happen to work, but it wouldn't be as appropriate.

Since the
@t is meant to represent temperatures, renaming it to @temps` would make the code less cryptic.

Code Snippets

print((sort {abs($a)<=>abs($b) || $b<=>$a} (split ' ',<>)[0..<>-1])[0] // 0)
use List::Util qw(max reduce);
use strict;

my $n = <>;
my @temps = (split ' ', <>)[0 .. $n - 1];
print((reduce { ($a, max($a, $b), $b)[1 + (abs($a) <=> abs($b))] } @temps)
       // 0);

Context

StackExchange Code Review Q#118830, answer score: 4

Revisions (0)

No revisions yet.