Perl Weekly Challenge 145: Dot Product

These are some answers to the Week 145 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 January 2, 2022 at 24:00). This blog post offers some solutions to this challenge, please don’t read on if you intend to complete the challenge on your own.

Task 1: Dot Product

You are given 2 arrays of same size, @a and @b.

Write a script to implement Dot Product.

Example:

@a = (1, 2, 3);
@b = (4, 5, 6);

$dot_product = (1 * 4) + (2 * 5) + (3 * 6) => 4 + 10 + 18 => 32

An important point is that we are told that the arrays have the same size. Thus, we don’t need to check that. We’ll also assume that they contain only numbers.

Dot Product in Raku

Raku is particularly well-suited to solve this task.

Assuming we have the two arrays presented in the task description (@a = (1, 2, 3); and @b = (4, 5, 6);), the infix Z zip operator will interleave the values from the two arrays like a zipper, taking index-corresponding elements from each operand:

say @a Z @b;  # prints: ((1 4) (2 5) (3 6))

That’s already quite good, but Z is also a metaoperator) that can be combined with another infix operator that will be applied to each pair of the above result. Thus, using Z*, we obtain directly the individual products:

say @a Z* @b; # prints: (4 10 18)

Finally, we only need to add these partial results (with the built-in sum function or method, or with another metaoperator combination, [+]) to fulfill the task in just one very short code-line:

use v6;

my @a = 1, 2, 3;
my @b = 4, 5, 6;

say sum @a Z* @b;  # Could also be: say [+] @a Z* @b;

This script displays the following output:

$ raku ./dot-product.raku
32

Dot Product in Perl

Since Perl doesn’t have the Zip operator and metaoperator, we cannot port the Raku solution to Perl. But, using a map, we can nevertheless implement a fairly concise solution:

use strict;
use warnings;
use feature "say";

my @a = (1, 2, 3);
my @b = (4, 5, 6);

my $result = 0;
$result += $_ for map { $a[$_] * $b[$_] } 0..$#a;
say $result;

This script displays the following output:

$ perl ./dot-product.pl
32

Update (Jan 2, 2022): Added the new section below with 16 languages.

Dot Product in 16 Other Programming Languages

All these implementations basically implement the same idea, with some minor differences due to syntactic differences.

In Julia

#!/usr/bin/julia

function dot_product(a1, a2)
    res = 0
    for i in eachindex(a1)
        res = res + a1[i] * a2[i]
    end
    return res
end

print( "Dot product: ", dot_product([1, 2, 3], [4, 5, 6]))

Output:

$ julia ./dot-product.jl
Dot product: 32

In Python

def dot_product (a1, a2):
    res = 0
    for i in range(0, len(a1)):
        res += a1[i] * a2[i]
    return res

product = dot_product([1, 2, 3], [4, 5, 6])
print(product)

Output:

$ python3 ./dot-product.py
32

In Scala

object dot_product extends App {
  val a1 = Array(1, 2, 3)
  val a2 = Array(4, 5, 6)
  var res = 0
  for (i <- a1.indices) {
    res = res + a1(i) * a2(i)
  }
  println("Dot product: " + res)
}

Output:

Dot product: 32

In Lua

#!/usr/bin/lua

local function dot_product(a1, a2)
    res = 0
    for i, v in next, a1 do
        res = res + v * a2[i]
    end
    return res
end

print(dot_product({1, 2, 3}, {4, 5, 6}))

Output:

$ lua ./dot-product.lua
32

In Kotlin

fun dot_product(a1: List<Int>, a2: List<Int>): Int {
    var res = 0
    for (i in a1.indices) {
        res += a1[i] * a2[i]
    }
    return res
}
fun main() {
    println (dot_product(listOf<Int>(1, 2, 3), listOf<Int>(4, 5, 6)))
}

Output:

32

In Ruby

def dot_product(a1, a2)
    res = 0
    a1.each_with_index do |v, i|
        res += v * a2[i]
    end
    return res
end
print dot_product([1, 2, 3], [4, 5, 6]), "\n"

Output:

bundle exec ruby dot-product.rb
32

In Rust

fn dot_product(a1: Vec<i32>, a2: Vec<i32>) -> i32 {
    let mut res = 0;
    a1.iter().enumerate().for_each(|(i, v)| {
        res += v * a2[i];
    });
    return res
}
fn main() {
    println!("{} ", dot_product(vec![1, 2, 3],vec![4, 5, 6]));
}

Output:

32

In Awk

$ echo '1 2 3
4 5 6 '  | awk -e '{
    for (i = 1; i <= NF; i ++) {
        if (NR == 1) {
             col[i] = $i
        }
        else {
            result += col[i] * $i
        }
    }
}
END {
    print result
}
'
32

In C

#include <stdio.h>

int main() {
    int a1[] = {1, 2, 3};
    int a2[] = {4, 5, 6};
    int res = 0;
    int size = sizeof(a1)/sizeof(a1[0]);
    for (int i = 0; i < size; i++) {
        res += a1[i] * a2[i];
    }
    printf("%d\n", res);
}

Output:

$ ./a.out
32

In Pascal

program dot_product;
const
    SIZE = 2;
var
    a1: array[0..SIZE] of integer = (1, 2, 3);
    a2: array[0..SIZE] of integer = (4, 5, 6);
    result, i : integer;
begin
    result := 0;
    for i := 0 to SIZE do
        result += a1[i] * a2[i];
    writeln(result);
end.

Output:

32

In D

import std.stdio;

int a1[] = [1, 2, 3];
int a2[] = [4, 5, 6];
int main() {
    int result = 0;
    for (int i = 0; i < 3; i++) {
        result += a1[i] * a2[i];
    }
    writeln(result);
    return 0;
}

Output:

32

In Bash

$ echo '1 2 3
4 5 6 '  | bash -c '
    read -a a1
    read -a a2
    for ((i = 0; i < ${#a1[@]}; i ++))
        do  ((result += a1[i] * a2[i]))
    done
    echo $result
'
32

In Dart

var a1 = [1, 2, 3];
var a2 = [4, 5, 6];

void main() {
    int result = 0;
    for (int i = 0; i < 3; i++ ) {
        result += a1[i] * a2[i];
    }
    print(result);
}

Output:

32

In Go

package main
import "fmt"

func main() {
    a1 := [3]int{1, 2, 3}
    a2 := [3]int{4, 5, 6}
    var result int = 0
    for i := 0; i < 3; i++ {
        result += a1[i] * a2[i]
    }
    fmt.Printf("Dot product: %d\n", result)
}

Output:

Dot product: 32

In Nim

Remember that Nim uses Python-like code indentation.

proc dot_product (a1, a2: array[0..2, int]) : int =
    result = 0
    for i in 0..2:
        result += a1[i] * a2[i]
    return result

let a1 = [1, 2, 3]
let a2 = [4, 5, 6]
let res = dot_product(a1, a2)
echo "dot product: ", res

Output:

dot product: 32

In Ring

Ring is a new programming language. Well, it is at least completely new to me (I had never heard about it until this morning of Jan 2, 2022), but it was released for the first time in January 2016. Its documentation states:

The Ring is an Innovative and practical general-purpose multi-paradigm scripting language that can be embedded in C/C++ projects, extended using C/C++ code and/or used as standalone language. The supported programming paradigms are Imperative, Procedural, Object-Oriented, Functional, Meta programming, Declarative programming using nested structures, and Natural programming.

I thought it would be interesting to use is as a guest language in the Perl Weekly Challenge to get a gist of it. Here we go.

see "Dot product: " + dot_product([1, 2, 3], [4, 5, 6]) + nl

func dot_product a1, a2
    res = 0
    for i = 1 to len(a1)
        res = res +  a1[i] * a2[i]
    next
    return res

Using the Ring online compiler, I obtain the following output:

Dot product = 32

Task 2: Palindromic Tree

This task requests us to implement a fairly complicated algorithm. I’ll try to come back to that later, if I have time and if I succeed to understand the requirement.

Update (Dec. 29): Finally, I have completed this second task here.

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 January 9, 2022. 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.