Is it safe to use integers as hash keys?
my %hash;
my $str = ...
for $str.NFC {
%hash{$_} = ...
}
A normal Hash coerces all its keys to strings:
my %a = '1' => 'foo', 2 => 'bar';
say %a.pairs.perl; # ("1" => "foo", "2" => "bar").Seq
Note how the second key became the string "2", even though it was originally passed to the Hash as an integer.
When you do hash look-ups, the subscript is also automatically converted to a string before it is used:
say %a{"2"}.perl; # "bar"
say %a{2}.perl; # "bar"
Note how the subscript 2 correctly found the element with key "2".
The conversion from integers to strings is well-defined in Perl 6, yielding a unique string for each unique integer, so the example you gave is fine.
If you don't want your Hash keys to be converted to strings, you can override the key handling using the {} notation in the declaration:
my %b{Any} = '1' => 'foo', 2 => 'bar';
say %b.pairs.perl; # ("1" => "foo", 2 => "bar").Seq
say %b{"1"}.perl; # "foo"
say %b{1}.perl; # Any
say %b{"2"}.perl; # Any
say %b{2}.perl; # "bar"
Note how in this case the second key 2 stays an integer, and doing a look-up with the string subscript "2" does not find it, nor does the subscript 1 find the entry with key "1".
%b{Any} means "accept keys of any type, and don't coerce them". This is sometimes called an 'object Hash' because it can map from any object to a value.
%b{Int} would mean "accept only Int keys, and don't coerce them". In this case you'll get an error if you even try to use anything that isn't already an Int.
Related
Imagine an object with strings as keys and their frequency count of occurrence as the value.
{"bravo charlie": 10, "alpha bravo charlie": 10, "delta echo foxtrot": 15, "delta echo": 7}
I am trying to optimize an algorithm such that A) any key that is a substring of another key AND has the same frequency value should be eliminated. The longer containing key should remain. B) Allow keys that are only a single word to remain even if contained by another key
The following pairwise comparison approach works but becomes very very slow on large objects. For example, an object with 560k keys is taking ~30 mins to complete the pairwise comparison:
// for every multi word key
// if a part of a longer key in candidates AND have same length delete
let keys = Object.keys(candidates)
.sort((a, b) => b.length - a.length)
.filter(key => {
if (key.split(" ").length === 1) {
return false
}
return true
});
// ^ order keys by length to speed up comparisons and filter out single words
checkKeyI: for (const keyi of keys) {
checkKeyJ: for (const keyj of keys) {
// because we pre-sorted if length is less then we are done with possible matches
if (keyj.length <= keyi.length) {
continue checkKeyI
}
// keys must not match exactly
if (keyj === keyi) {
continue checkKeyJ
}
// keyi must be a substring of keyj
if (!keyj.includes(keyi)) {
continue checkKeyJ
}
// they must have the same freq occurr values
if (candidates[keyj] === candidates[keyi]) {
delete candidates[keyi]
continue checkKeyI
}
}
}
The desired result would be:
{"alpha bravo charlie": 10, "delta echo foxtrot": 15, "delta echo": 7}
because bravo charlie was eliminated. Are they any obvious or clever ways to speed this up?
Sort the keys based on their length (use alphabetical ordering as a tie-breaker)
Starting with the longest key (and going in a descending order of length), add the keys to a trie. The leaf node for the key will store the value.
For inserting a key, start traversing the trie. If the key has a single word, insert it. If it has multiple words, and a match is found with the same frequency, don't insert it.
This will reduce your complexity from O(n^2) to O(n*m), where n = number of strings and m = length of longest string
I do not understand why $test is '1' and not 'foo'. Is there any way to write 'foo' (written in %hash) to $test?
#/usr/bin/perl
use warnings;
use strict;
use utf8;
my %hash;
$hash{'test'} = {'foo' => 1};
print keys %{$hash{'test'}}; #foo
print "\n";
print values %{$hash{'test'}}; #1
print "\n";
print %{$hash{'test'}}; #foo1
print "\n";
# here is the part I do not understand
my $test = keys %{$hash{'test'}};
print "$test\n"; #1 but why? I was expecting #foo
How can I push 'foo' in $test?
keys() returns a list. But your assignment to a scalar (my $test = keys ...) puts it into scalar context. Therefore it is evaluated to the length of the list, which is 1 in your case.
my $test = keys %{$hash{'test'}};
When assigning a list like keys returns to a scalar, what's assigned is the length of the list. Which is 1 in this case.
When the return value from a Perl function confuses you, it's always worth checking the documentation for that function - paying particular attention to the section talking about how the return value varies in different contexts.
The very start of perldoc -f keys says this (emphasis mine):
keys HASH
keys ARRAY
Called in list context, returns a list consisting of all the keys of the named hash, or in Perl 5.12 or later only, the indices of an array. Perl releases prior to 5.12 will produce a syntax error if you try to use an array argument. In scalar context, returns the number of keys or indices.
You're assigning the results of the expression to a scalar variable. The expression is, therefore, evaluated in scalar context and you get the number of keys in the hash (i.e. 1).
To fix that, force the expression to be evaluated in list context by putting parentheses around the variable:
my ($test) = keys %{$hash{'test'}};
Following this paper, I'm trying to create an associative array like this:
variables
{
char[30] translate[ char[] ];
}
It is exactly the same example in the paper. Problem comes when I try to put values to this associative array. For example:
on preStart
{
translate["hello"] = "hola";
}
That gives me a compilation error: "Error 1112 at (89,23): operand types are incompatible"
What I'm doing wrong?
VERSIONS: I'm using Vector CAPL Browser included with CANalyzer version 11.0 SP2
With associative fields (so-called maps) you can perform a 1:1 assignment of values to other values without using excessive memory. The elements of an associative field are key value pairs, whereby there is fast access to a value via a key.
An associative field is declared in a similar way to a normal field but the data type of the key is written in square brackets:
int m[float]; // maps floats to ints
float x[int64]; // maps int64s to floats
char[30] s[ char[] ] // maps strings (of unspecified length) to strings of length < 30
If the key type char[] is specified, all the character fields (of any size) can be used as key values. In the iteration the loop variable must then also be declared as char[]. Key comparisons, e.g. in order to determine iteration sequence, are then performed as character string comparisons, whereby no country-specific algorithms are used.
char[] is the only field type that can be used as a key type. Please bear in mind that you can not declare variables or parameters of the char[] type, with the exception of loop variables in the iteration.
Association between strings:
char[30] namen[char []];
strncpy(namen["Max"], "Mustermann", 30);
strncpy(namen["Vector"], "Informatik", 30);
for (char[] mykey : namen)
{
write("%s is mapped to %s", mykey, namen[mykey]);
}
I am using Horde_Text_Diff to compute the difference between two strings. Sample code is as follows:
$check_diff = new Horde_Text_Diff( 'auto', array('asdf','asd11') );
$renderer = new Horde_Text_Diff_Renderer_Inline();
echo $renderer->render($check_diff);
This echoes nothing. The correct behaviour would be to show a difference at character 4.
If I change the comparison array from array('asdf','asd11') to, for instance, array('asdf','12345'), then it will output a1. In other words, it seems only to be comparing the first character. Any ideas?
When I try this, I get two warnings:
PHP Warning: array_walk() expects parameter 1 to be array, string given in /usr/share/php/Horde/Text/Diff/Engine/Native.php on line 33
PHP Warning: array_walk() expects parameter 1 to be array, string given in /usr/share/php/Horde/Text/Diff/Engine/Native.php on line 34
I.e., something is getting strings where it expects arrays.
That's because, rather than passing (an array containing) two strings to Horde_Text_Diff(), you should pass (an array containing) two arrays-of-strings (where each string represents a line of text).
If the actual strings you're currently trying to pass in contain multiple lines of text, then you can split them into arrays-of-strings using explode(), e.g.:
$a = "foo\nbar\nbaz";
$b = "foo\nqux\nbaz";
$a_lines = explode("\n", $a);
$b_lines = explode("\n", $b);
$check_diff = new Horde_Text_Diff( 'auto', array($a_lines, $b_lines) );
$renderer = new Horde_Text_Diff_Renderer_Inline();
echo $renderer->render($check_diff);
which outputs:
foo
<del>bar</del><ins>qux</ins>
baz
I have been learning Groovy for a week, but I have a problem: I need to store the combinations of words to the corresponding ascii values. For example the combinations of the word "the" and the sum of the ascii values of each characters is 10(not exactly,but still for example).
There are, obviously, other words where the sum of ascii values is 10. I want a data structure where I can look up the value 10 and get the words where the sum of the ascii values is 10. I can't do this in Groovy Map, because the key value should be unique. How to do this in Groovy?
You could do something like this as well:
def words = [ 'the', 'het', 'love', 'groovy' ]
words.groupBy { ( it as char[] ).collect { it as int }.sum() }
That gives you the map:
[321:[the, het], 438:[love], 678:[groovy]]
Here's a small example that initiates a Map that returns an empty List in case a key is requested for the first time using the withDefault method:
def map = [:].withDefault { [] }
map[10] << 'the'
map[10] << 'as'
map[20] << 'from'
assert map[10] == ['the', 'as']
assert map[20] == ['from']
A map of Int to List of String should fix this. Simply append the words having the same sum, to the list corresponding to a key.
You need a multi-map data structure. One can be found in Apache Commons, another one in Guava.
In Groovy you can use a simple Map of int -> list, with default value for convenience.