The Bear's Den

Enter at your own risk

There Is More Than One Way To Power

Task 1: Reverse String

Submitted by: Mohammad Sajid Anwar


You are given a string.

Write a script to reverse the given string without using standard reverse function.

Example 1

Input: $str = ""
Output: ""

Example 2

Input: $str = "reverse the given string"
Output: "gnirts nevig eht esrever"

Example 3

Input: $str = "Perl is Awesome"
Output: "emosewA si lreP"

Example 4

Input: $str = "v1.0.0-Beta!"
Output: "!ateB-0.0.1v"

Example 5

Input: $str = "racecar"
Output: "racecar"

Solution

I’ll regard reverse as the standard reverse function in Perl and “Reverse” |. as the standard primitive verb in J. Any other way to reverse a string shall count as a valid solution to this task.

There is quite a number of approaches:

Perl

Some variants:

use strict;
use warnings;
use experimental 'signatures';
use List::Gather;
use PDL;
use PDL::Char;
use PDL::NiceSlice;

sub reverse_slice ($str) {
    join '', (split //, $str)[map -$_, 1 .. length $str];
}

# gather/take instead of push
sub reverse_pop ($str) {
    my @arr = split //, $str;
    join '', gather {take pop @arr while @arr};
}

# like shift/unshift
sub reverse_substr ($str) {
    my $rev = '';
    substr($rev, 0, 0) = substr $str, 0, 1, '' while length $str;
    $rev;
}

# avoid copying
sub reverse_inplace ($str) {
    substr($str, 0, 0) = substr $str, $_, 1, '' for 1 .. length($str);
    $str;
}

# natively fails on an empty string
sub reverse_pdl ($str) {
    length($str) ? PDL::Char->new($str)->(-1:0)->atstr(0) : '';
}

See the full solution to task 1.

J

rev_from is almost identical to reverse_slice: pick negative indices that count from the end of the string.

In rev_substr, the substring is taken from the start to the end in reverse order __.

rev_from =: {~ -@>:@i.@# : [:
rev_substr =: __&(];.0)

Task 2: Armstrong Number

Submitted by: Mohammad Sajid Anwar


You are given two integers, $base and $limit.

Write a script to find all Armstrong numbers in base $base that are less than $limit.

If raising each of the digits of a nonnegative integer to the power of the total number of digits, then taking the sum, equals the original number, it is an Armstrong number.

Example 1

Input: $base = 10, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407)

Example 2

Input: $base = 7, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 10, 25, 32, 45, 133, 134, 152, 250)

Example 3

Input: $base = 16, $limit = 1000
Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 342, 371, 520, 584, 645)

Solution

Following a brute-force approach:
Calculate the digit power sum in base B for all numbers from zero to the limit and pick those where it equals the number itself.

Perl

Using a lexical sub to calculate the digit power sum.

use v5.26;
use warnings;
use experimental 'signatures';
use Math::Prime::Util qw(todigits vecsum);

sub armstrong ($base, $limit) {
    my sub dps {
        my @digits = todigits shift, $base;
        vecsum map $_ ** @digits, @digits;
    }

    grep $_ == dps($_), 0 .. $limit;
}

See the full solution to task 2.

J

The same in J with one minor difference: dps is not called as a verb but resolved to its value when a verb is created as m armstrong.

armstrong =: adverb define
   dps =. ([: +/ ] ^ #) @ (m&#.inv)
   ((] #~ ] = dps"0) @: i. @ >:) f. : [:
)
   10 armstrong
(] #~ ] = ([: +/ ] ^ #)@(10&#.^:_1)"0)@:i.@>: :[:
   10 armstrong 1000
0 1 2 3 4 5 6 7 8 9 153 370 371 407