Dating agency

From Rosetta Code
Dating agency is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Scenario

A sailor signs up to a dating agency hoping to find a suitable mate.

The dating agency has 10 ladies on its books who may be suitable and uses the algorithm "all the nice girls love a sailor" to decide which ones to put forward for the sailor's consideration.

The sailor uses a different algorithm ("lady is lovable") to decide which ladies to actually date.

Task

Model this scenario in your language.

Give the sailor and ladies some names.

Use some arbitrary method based on the ladies' names to determine whether they're nice and/or lovable. For preference, choose a method where the outcomes are (more or less) equally likely.

Hence, determine which ladies the dating agency should suggest and which of these the sailor should offer to date.

Note

This task is intended as a bit of fun as well as a simple exercise in object modelling so hopefully it won't offend anyone!

11l

Translation of: Python
V sailors = [‘Adrian’, ‘Caspian’, ‘Dune’, ‘Finn’, ‘Fisher’, ‘Heron’, ‘Kai’, ‘Ray’, ‘Sailor’, ‘Tao’]

V ladies = [‘Ariel’, ‘Bertha’, ‘Blue’, ‘Cali’, ‘Catalina’, ‘Gale’, ‘Hannah’, ‘Isla’, ‘Marina’, ‘Shelly’]

F isnicegirl(s)
   R Int(s[0].code) % 2 == 0

F islovable(slady, ssailor)
   R Int(slady.last.code) % 2 == Int(ssailor.last.code) % 2

L(lady) ladies
   I isnicegirl(lady)
      print(‘Dating service should offer a date with ’lady)
      L(sailor) sailors
         I islovable(lady, sailor)
            print(‘    Sailor ’sailor‘ should take an offer to date her.’)
   E
      print(‘Dating service should NOT offer a date with ’lady)
Output:
Dating service should NOT offer a date with Ariel
Dating service should offer a date with Bertha
    Sailor Dune should take an offer to date her.
    Sailor Kai should take an offer to date her.
    Sailor Ray should take an offer to date her.
    Sailor Tao should take an offer to date her.
Dating service should offer a date with Blue
    Sailor Dune should take an offer to date her.
    Sailor Kai should take an offer to date her.
    Sailor Ray should take an offer to date her.
    Sailor Tao should take an offer to date her.
Dating service should NOT offer a date with Cali
Dating service should NOT offer a date with Catalina
Dating service should NOT offer a date with Gale
Dating service should offer a date with Hannah
    Sailor Adrian should take an offer to date her.
    Sailor Caspian should take an offer to date her.
    Sailor Finn should take an offer to date her.
    Sailor Fisher should take an offer to date her.
    Sailor Heron should take an offer to date her.
    Sailor Sailor should take an offer to date her.
Dating service should NOT offer a date with Isla
Dating service should NOT offer a date with Marina
Dating service should NOT offer a date with Shelly

Factor

Works with: Factor version 0.99 2021-06-02
USING: formatting io kernel math math.primes math.vectors
prettyprint qw sequences sets ;

CONSTANT: ladies qw{
    Abigail Emily Haley Leah Maru
    Penny Caroline Jodi Marnie Robin
}

CONSTANT: sailor "Willy"

: prime-root? ( n -- ? ) 1 - 9 mod 1 + prime? ;                    ! is the digital root prime?
: nice? ( lady sailor -- ? ) [ hashcode ] bi@ + prime-root? ;      ! a lady is nice if the sum of the hashcode of her name and the sailor's name has a prime digit root
: lovable? ( lady sailor -- ? ) vdot prime-root? ;                 ! a lady is lovable if the dot product of her name and the sailor's name has a prime digital root

: nice ( -- seq ) ladies [ sailor nice? ] filter ;
: lovable ( -- seq ) ladies [ sailor lovable? ] filter ;
: compatible ( -- seq ) nice lovable intersect ;
: ladies. ( seq -- ) ", " join print ;

"lady       nice?      lovable?" print
"------------------------------" print
ladies [
    dup sailor [ nice? ] [ lovable? ] bi-curry bi
    [ "yes" "no" ? ] bi@
    "%-10s %-10s %-10s\n" printf
] each nl

"Based on this analysis:" print nl
"The dating agency should suggest the following dates:" print
nice ladies. nl
sailor "And %s should offer to date these ones:\n" printf
lovable ladies. nl
sailor "But just between us, only the following ladies are compatible with %s:\n" printf
compatible ladies.
Output:
lady       nice?      lovable?
------------------------------
Abigail    no         yes       
Emily      no         no        
Haley      no         no        
Leah       no         no        
Maru       yes        no        
Penny      yes        no        
Caroline   no         yes       
Jodi       no         yes       
Marnie     yes        no        
Robin      yes        yes       

Based on this analysis:

The dating agency should suggest the following dates:
Maru, Penny, Marnie, Robin

And Willy should offer to date these ones:
Abigail, Caroline, Jodi, Robin

But just between us, only the following ladies are compatible with Willy:
Robin

FreeBASIC

Translation of: Phix
Function digitalRoot(n As Integer) As Integer
    Assert(n >= 0)
    While n > 9
        Dim As Integer tot = 0
        While n > 0
            tot += (n Mod 10)
            n \= 10
        Wend
        n = tot
    Wend
    Return n
End Function

Type Lady 
    nombre As String
    Declare Function lovable() As Integer
    Declare Function love(s As Any Ptr) As Integer
End Type

Function Lady.lovable() As Integer
    Return (Asc(this.nombre) Mod 2)
End Function

Function Lady.love(s As Any Ptr) As Integer
    Return (digitalRoot(Asc(this.nombre)) > 4)
End Function

Function newLady(nombre As String) As Lady
    Dim As Lady l
    l.nombre = nombre
    Return l
End Function

Function ladyNames(ladies() As Lady) As String
    Dim As String res = ""
    For i As Integer = Lbound(ladies) To Ubound(ladies)
        If ladies(i).nombre <> "" Then res &= ladies(i).nombre & ", "
    Next i
    Return Left(res, Len(res)-2) ' remove last comma and space
End Function

Type Marinero
    nombre As String
    Declare Function love(l As Any Ptr) As Integer
End Type

Function Marinero.love(l As Any Ptr) As Integer
    Return Cast(Lady Ptr, l)->lovable()
End Function

Dim As String nombres(9) = {"Ada", "Crystal", "Elena", "Euphoria", "Janet", "Julia", "Lily", "Miranda", "Perl", "Ruby"}
Dim As Lady ladies(Ubound(nombres))
For i As Integer = Lbound(nombres) To Ubound(nombres)
    ladies(i) = newLady(nombres(i))
Next i
Dim As Marinero sailor
sailor.nombre = "Pascal"
Dim As Integer eligiblesCount = 0
Dim As Lady eligibles(Ubound(ladies))

For i As Integer = Lbound(ladies) To Ubound(ladies)
    If ladies(i).love(@sailor) Then 
        eligibles(eligiblesCount) = ladies(i)
        eligiblesCount += 1
    End If
Next i

Print "lady       loves sailor  lovable"
Print "----       ------------  -------"

For i As Integer = Lbound(ladies) To Ubound(ladies)
    Print Using "\      \   &         &"; ladies(i).nombre; Iif(ladies(i).love(@sailor), "True ", "False"); Iif(ladies(i).lovable(), "True ", "False")
Next i

Print !"\nBased on this analysis:"
Print "The dating agency should suggest the following ladies:"
Print ladyNames(eligibles())

Print !"\nand "; sailor.nombre; " should offer to date these ones:"
Print ladyNames(eligibles())

Sleep

Julia

Translation of: Python
sailors = ["Adrian", "Caspian", "Dune", "Finn", "Fisher", "Heron", "Kai",
           "Ray", "Sailor", "Tao"]

ladies = ["Ariel", "Bertha", "Blue", "Cali", "Catalina", "Gale", "Hannah",
           "Isla", "Marina", "Shelly"]

isnicegirl(s) =  Int(s[begin]) % 2 == 0

islovable(slady, ssailor) =  Int(slady[end]) % 2 == Int(ssailor[end]) % 2

for lady in ladies
    if isnicegirl(lady)
        println("Dating service should offer a date with ", lady)
        for sailor in sailors
            if islovable(lady, sailor)
                println("    Sailor ", sailor, " should take an offer to date her.")
            end
        end
    else
        println("Dating service should NOT offer a date with ", lady)
    end
end
Output:
Dating service should NOT offer a date with Ariel
Dating service should offer a date with Bertha
    Sailor Dune should take an offer to date her.
    Sailor Kai should take an offer to date her.
    Sailor Ray should take an offer to date her.
    Sailor Tao should take an offer to date her.
Dating service should offer a date with Blue
    Sailor Dune should take an offer to date her.
    Sailor Kai should take an offer to date her.
    Sailor Ray should take an offer to date her.
    Sailor Tao should take an offer to date her.
Dating service should NOT offer a date with Cali
Dating service should NOT offer a date with Catalina
Dating service should NOT offer a date with Gale
Dating service should offer a date with Hannah
    Sailor Adrian should take an offer to date her.
    Sailor Caspian should take an offer to date her.
    Sailor Finn should take an offer to date her.
    Sailor Fisher should take an offer to date her.
    Sailor Heron should take an offer to date her.
    Sailor Sailor should take an offer to date her.
Dating service should NOT offer a date with Isla
Dating service should NOT offer a date with Marina
Dating service should NOT offer a date with Shelly

Perl

Translation of: Raku
use strict;
use warnings;

use Digest::SHA qw(sha1_hex);
use List::Util <max head>;

my(%laddies,%taylors);

for my $name ( qw( Adam Bob Conrad Drew Eddie Fred George Harry Ian Jake Ken Larry Mike
                   Ned Oscar Peter Quincy Richard Sam Tom Uriah Victor Will Yogi Zach ) ) {
    $laddies{$name}{loves}   = hex substr sha1_hex($name),  0, 4;
    $laddies{$name}{lovable} = hex substr sha1_hex($name), -4, 4;
}

for my $name ( < Elizabeth Swift Rip > ) {
    $taylors{$name}{loves}   = hex substr sha1_hex($name),  0, 4;
    $taylors{$name}{lovable} = hex substr sha1_hex($name), -4, 4;
}

sub rank_by {
   my($subk,$k,$t,$l) = @_;
   sort { abs $$t{$k}{$subk} - $$l{$a}{$subk} <=> abs $$t{$k}{$subk} - $$l{$b}{$subk} } keys %$l;
}

for my $taylor (sort keys %taylors) {
    printf "%9s will like: %s\n", $taylor, join ', ', my @likes = head 10, rank_by('loves',  $taylor, \%taylors, \%laddies);
    printf "        Is liked by: %s\n",    join ', ', my @liked = head 10, rank_by('lovable',$taylor, \%taylors, \%laddies);

    my($max,%matches) = 0;
    $matches{$liked[$_]}  = @liked-$_ for reverse 0..$#liked;
    $matches{$likes[$_]} += @likes-$_ for reverse 0..$#likes;
    $matches{$_} < $max or $max = $matches{$_} for keys %matches;
    print 'Best match(s): ' . join(', ', sort grep { $matches{$_} == $max } keys %matches) . "\n\n";
}
Output:
Elizabeth will like: Adam, Conrad, Ian, Jake, Larry, Bob, Drew, Quincy, Mike, Victor
        Is liked by: Ian, Mike, Uriah, Jake, Tom, Richard, Drew, George, Victor, Larry
Best match(s): Ian

      Rip will like: Jake, Larry, Bob, Drew, Quincy, Mike, Adam, Conrad, Ian, Victor
        Is liked by: Ned, Will, Eddie, Oscar, Quincy, Conrad, George, Richard, Jake, Uriah
Best match(s): Jake, Quincy

    Swift will like: Ken, Oscar, Zach, Ned, Sam, Tom, Will, Yogi, Fred, Uriah
        Is liked by: Ken, Adam, Yogi, Sam, Larry, Victor, Drew, Bob, Tom, Ian
Best match(s): Ken

Phix

Translation of: Wren

Nowt fancy here, but at least I made some notes as I was translating things:
Fields are private by default and need to be made explicitly public, whereas methods are the other way round and need to be made explicitly private when that is wanted. Phix classes don't really play nice with functions like apply and filter, hence the need for several little shims (new_lady/lady_names/sailor_love). There is no equivalent to the implicit "toString", and no plans for one, though you could easily write an explicit one. The "Lady lady = ladies[i]; lady.name" programming style is generally preferred: trying to use say "ladies[i].name" simply will not work, instead "class x = ladies[i]; x.name" is the correct way to write generic class code in Phix, should you genuinely need that. No attempt has even been started on transpilation of class based code to JavaScript. Booleans in Phix are just integers, and need to be printed with %t rather than %s. Of course all this is open source, yada-yada-ya.

without javascript_semantics
include structs.e

function digital_root(integer n)
    assert(n>=0)
    while n>9 do
        integer tot = 0
        while n>0 do
            tot += remainder(n,10)
            n = floor(n/10)
        end while
        n = tot
    end while
    return n
end function

class Lady 
    public string name
    function lovable()
        return odd(sum(name))
    end function
    function love(class s)
        if get_struct_name(s)!="Sailor" then return null end if // indeterminate
        return digital_root(sum(name)) > 4
    end function
end class

function new_lady(string name)
    return new(Lady,{name})
end function

function lady_names(sequence ladies)
    sequence res = repeat(0,length(ladies))
    for i=1 to length(res) do
        Lady lady = ladies[i]
        res[i] = lady.name
    end for
    return join(res,", ")
end function

class Sailor
    public string name
    function love(Lady l)
        if get_struct_name(l)!="Lady" then return null end if // indeterminate
        return l.lovable()
    end function
end class

constant names = {"Ada", "Crystal", "Elena", "Euphoria", "Janet", "Julia", "Lily", "Miranda", "Perl", "Ruby"},
         ladies = apply(names,new_lady)
Sailor sailor = new({"Pascal"})
sequence eligibles = {}

function sailor_love(Lady lady)
    return sailor.love(lady)
end function

printf(1,"%-10s  %-12s  %s\n", {"lady", "loves sailor", "lovable"})
printf(1,"%-10s  %-12s  %s\n", {"----", "------------", "-------"})
for i=1 to length(ladies) do
    Lady lady = ladies[i]
    bool lovesSailor = lady.love(sailor)
    if lovesSailor then eligibles = append(eligibles,lady) end if
    printf(1,"%-10s  %-12t  %t\n", {lady.name, lovesSailor, lady.lovable()})
end for
printf(1,"\nBased on this analysis:")
printf(1,"\nThe dating agency should suggest the following ladies:\n")
printf(1,"%s\n",lady_names(eligibles))
printf(1,"\nand %s should offer to date these ones:\n",{sailor.name})
printf(1,"%s\n",lady_names(filter(eligibles,sailor_love)))
Output:
lady        loves sailor  lovable
----        ------------  -------
Ada         false         false
Crystal     true          false
Elena       true          true
Euphoria    false         true
Janet       false         false
Julia       true          true
Lily        true          false
Miranda     true          false
Perl        true          true
Ruby        false         false

Based on this analysis:
The dating agency should suggest the following ladies:
Crystal, Elena, Julia, Lily, Miranda, Perl

and Pascal should offer to date these ones:
Elena, Julia, Perl


Python

"""
Jokes about courtesans aside, the selection process is by the letters of the names.
If the integer corresponding to the ASCII character of the first letter of the
name is even, the woman is considered nice. If the integers of the last letter of
the lady's name and the sailor's name are both odd or both even, the sailor should
consider the lady as lovable.
"""

sailors = ['Adrian', 'Caspian', 'Dune', 'Finn', 'Fisher', 'Heron', 'Kai',
           'Ray', 'Sailor', 'Tao']

ladies = ['Ariel', 'Bertha', 'Blue', 'Cali', 'Catalina', 'Gale', 'Hannah',
           'Isla', 'Marina', 'Shelly']

def isnicegirl(s):
    return ord(s[0]) % 2 == 0

def islovable(slady, ssailor):
    return ord(slady[-1]) % 2 == ord(ssailor[-1]) % 2

for lady in ladies:
    if isnicegirl(lady):
        print("Dating service should offer a date with", lady)
        for sailor in sailors:
            if islovable(lady, sailor):
                print("    Sailor", sailor, "should take an offer to date her.")
    else:
        print("Dating service should NOT offer a date with", lady)
Output:
Dating service should NOT offer a date with Ariel
Dating service should offer a date with Bertha
    Sailor Dune should take an offer to date her.
    Sailor Kai should take an offer to date her.
    Sailor Ray should take an offer to date her.
    Sailor Tao should take an offer to date her.
Dating service should offer a date with Blue
    Sailor Dune should take an offer to date her.
    Sailor Kai should take an offer to date her.
    Sailor Ray should take an offer to date her.
    Sailor Tao should take an offer to date her.
Dating service should NOT offer a date with Cali
Dating service should NOT offer a date with Catalina
Dating service should NOT offer a date with Gale
Dating service should offer a date with Hannah
    Sailor Adrian should take an offer to date her.
    Sailor Caspian should take an offer to date her.
    Sailor Finn should take an offer to date her.
    Sailor Fisher should take an offer to date her.
    Sailor Heron should take an offer to date her.
    Sailor Sailor should take an offer to date her.
Dating service should NOT offer a date with Isla
Dating service should NOT offer a date with Marina
Dating service should NOT offer a date with Shelly

Raku

Welcome to the Arbitrary and Capricious Dating Agency, (We don't use zodiacs, but we're just as arbitrary!)

use Digest::SHA1::Native;

my %ladies = < Alice Beth Cecilia Donna Eunice Fran Genevieve Holly Irene Josephine Kathlene Loralie Margaret
               Nancy Odelle Pamela Quinci Rhonda Stephanie Theresa Ursula Victoria Wren Yasmine Zoey >.map: -> $name {
    $name => {
        loves   => :16(sha1-hex($name).substr(0,4)),
        lovable => :16(sha1-hex($name).substr(*-4))
    }
}

my %sailors = < Ahab Brutus Popeye >.map: -> $name {
    $name => {
        loves   => :16(sha1-hex($name).substr(0,4)),
        lovable => :16(sha1-hex($name).substr(*-4))
    }
}

for %sailors.sort( *.key ) -> $sailor {
    printf "%6s will like: ", $sailor.key;
    say join ', ', my @likes = %ladies.sort( { abs $sailor.value.<loves> - .value.<loves> } ).head(10)».key;
    print '     Is liked by: ';
    say join ', ',  my @liked = %ladies.sort( { abs $sailor.value.<lovable> - .value.<lovable> } ).head(10)».key;
    my %matches;
    for @liked.reverse Z, (1..10) { %matches{.[0]} += .[1] };
    for @likes.reverse Z, (1..10) { %matches{.[0]} += .[1] };
    say 'Best match(s): ' ~ %matches.grep(*.value == %matches.values.max)».key.sort.join(', ');
    say '';
}
  Ahab will like: Eunice, Ursula, Irene, Holly, Fran, Genevieve, Zoey, Donna, Cecilia, Alice
     Is liked by: Nancy, Theresa, Victoria, Genevieve, Alice, Rhonda, Kathlene, Odelle, Eunice, Pamela
Best match(s): Eunice, Genevieve

Brutus will like: Beth, Nancy, Quinci, Odelle, Josephine, Kathlene, Theresa, Rhonda, Wren, Margaret
     Is liked by: Yasmine, Margaret, Loralie, Holly, Wren, Fran, Quinci, Eunice, Alice, Victoria
Best match(s): Quinci

Popeye will like: Loralie, Theresa, Stephanie, Yasmine, Alice, Cecilia, Donna, Zoey, Nancy, Genevieve
     Is liked by: Loralie, Margaret, Holly, Wren, Yasmine, Fran, Quinci, Eunice, Alice, Victoria
Best match(s): Loralie

Wren

Library: Wren-math
Library: Wren-fmt
import "./math" for Int, Nums
import "./fmt" for Fmt

class Lady {
    construct new(name) {
        _name = name
    }

    name { _name }

    // Sum the ASCII values of the characters in the ladies' names and find the digital root.
    // If it's more than 4, they're 'nice'.
    nice {
        var sum = Nums.sum(_name.bytes)
        return Int.digitalRoot(sum)[0] > 4
    }

    // Sum the ASCII values of the characters in the ladies' names.
    // If it's odd, they're 'lovable'.
    lovable {
        var sum = Nums.sum(_name.bytes)
        return sum % 2 == 1
    }

    love(s) {
        if (!(s is Sailor)) return null // indeterminate
        return nice
    }

    toString { _name }
}

class Sailor {
    construct new(name) {
        _name = name
    }

    name { _name }

    love(l) {
        if (!(l is Lady)) return null // indeterminate
        return l.lovable
    }

    toString { _name }
}

var names = ["Ada", "Crystal", "Elena", "Euphoria", "Janet", "Julia", "Lily", "Miranda", "Perl", "Ruby"]
var ladies = names.map { |n| Lady.new(n) }.toList
var sailor = Sailor.new("Pascal")
var eligibles = []
var format = "$-10s  $-12s  $s"
Fmt.print(format, "lady", "loves sailor", "lovable")
Fmt.print(format, "----", "------------", "-------")
for (lady in ladies) {
    var lovesSailor = lady.love(sailor)
    if (lovesSailor) eligibles.add(lady)
    Fmt.print(format, lady, lovesSailor, lady.lovable)
}
System.print("\nBased on this analysis:")
System.print("\nThe dating agency should suggest the following ladies:")
System.print(eligibles)
System.print("\nand %(sailor) should offer to date these ones:")
System.print(eligibles.where { |e| sailor.love(e) }.toList)
Output:
lady        loves sailor  lovable
----        ------------  -------
Ada         false         false
Crystal     true          false
Elena       true          true
Euphoria    false         true
Janet       false         false
Julia       true          true
Lily        true          false
Miranda     true          false
Perl        true          true
Ruby        false         false

Based on this analysis:

The dating agency should suggest the following ladies:
[Crystal, Elena, Julia, Lily, Miranda, Perl]

and Pascal should offer to date these ones:
[Elena, Julia, Perl]