Perl Weekly Challenge 267: Line Count

These are some answers to the Week 267, Task 2, of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few days from now (on May 5, 2024 at 23:59). This blog post provides some solutions to this challenge. Please don’t read on if you intend to complete the challenge on your own.

Task 2: Line Count

You are given a string, $str, and a 26-items array @widths containing the width of each character from a to z.

Write a script to find out the number of lines and the width of the last line needed to display the given string, assuming you can only fit 100 width units on a line.

Example 1

Input: $str = "abcdefghijklmnopqrstuvwxyz"
       @widths = (10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10)
Output: (3, 60)

Line 1: abcdefghij (100 pixels)
Line 2: klmnopqrst (100 pixels)
Line 3: uvwxyz (60 pixels)

Example 2

Input: $str = "bbbcccdddaaa"
       @widths = (4,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10)
Output: (2, 4)

Line 1: bbbcccdddaa (98 pixels)
Line 2: a (4 pixels)

Line Count in Raku

The first step is to build a hash, %widths, mapping each letter of the alphabet to its width. Then, we iterate over the letters of the input string: we keep adding letters to the current line so long as the line is not more than 100, or we start a new line.

sub line-count(@in_widths, $in_str) {
    my $i = 0;
    my %widths;
    for 'a'..'z' -> $let {
        %widths{$let} = @in_widths[$i++];
    }
    my $line-count = 1;
    my $line-width = 0;
    for $in_str.comb -> $let {
        my $tmp = $line-width + %widths{$let};
        if $tmp <= 100 {
            $line-width = $tmp;
        } else {
            $line-count++;
            $line-width = %widths{$let};
        }
    }
    return "($line-count, $line-width)";
}            

my @tests = (10 xx 26, "abcdefghijklmnopqrstuvwxyz"),
            ([4,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
            10,10,10,10,10,10,10,10,10,10,10], "bbbcccdddaaa"); 

for @tests -> @test {
    printf "%-28s => ", @test[1];
    say line-count @test[0], @test[1];
}

This program displays the following output:

$ raku ./line-count.raku
abcdefghijklmnopqrstuvwxyz   => (3, 60)
bbbcccdddaaa                 => (2, 4)

Line Count in Perl

This is a port to Perl of the above Raku program. We first build a hash to map letters to their width, and then iterate over the letters of the input string to fill lines with length not more than 100.

use strict;
use warnings;
use feature 'say';

sub line_count {
    my @in_widths = @{$_[0]};
    my $in_str = $_[1];
    my $i = 0;
    my %widths;
    for my $let ('a'..'z') {
        $widths{$let} = $in_widths[$i++];
    }
    my $line_count = 1;
    my $line_width = 0;
    for my $let (split //, $in_str) {
        my $tmp = $line_width + $widths{$let};
        if ($tmp <= 100) {
            $line_width = $tmp;
        } else {
            $line_count++;
            $line_width = $widths{$let};
        }
    }
    return "($line_count, $line_width)";
}            

my @tests = ( [[10,10,10,10,10,10,10,10,10,10,10,10,10,10,
                10,10,10,10,10,10,10,10,10,10,10,10], 
                "abcdefghijklmnopqrstuvwxyz"],
              [[4,10,10,10,10,10,10,10,10,10,10,10,10,10,
                10,10,10,10,10,10,10,10,10,10,10,10], 
                "bbbcccdddaaa"]); 

for my $test (@tests) {
    printf "%-28s => ", $test->[1]; 
    say line_count $test->[0], $test->[1];
}

This program displays the following output:

$ perl ./line_count.pl
abcdefghijklmnopqrstuvwxyz   => (3, 60)
bbbcccdddaaa                 => (2, 4)

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on May 12, 2024. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.