Perl: Using hash instead of array - arrays

while ($word = <STDIN>) {
$length = length($word) -1; # Subtract 1 for included newline char
$wordLength[$length]++;
}
print "Word length \t\t Occurrences \n\n";
for ( my $i =1; $i <= $#wordLength; $i++ ) {
if (not exists $wordLength[$i]) {
print "$i \t\t\t 0 \n";
}
else {
print "$i \t\t\t $wordLength[$i] \n";
}
}
This works great reading in a txt file and outputting as such:
Word Length Occurrence
1 27
2 104
3 1039
4 3505
5 7181
6 11765
7 15898
I am trying to get this to work using hash instead of an array but it doesn't seem to work. This is my attempt:
while ($word = <STDIN>) {
chomp($word);
$length = length($word);
$wordLength{$word} = "$length";
}
foreach $word (sort keys %wordLength) {
print "$word, $wordLength{$word}\n"; # print key and value
}

Why? Any array works great here.
my #occurrences_by_length;
while (my $word = <>) {
chomp($word);
my $length = length($word);
++$occurrences_by_length[$length];
}
print "Length Occurrences\n";
for my $length (1..$#occurrences_by_length) {
my $occurrences = $occurrences_by_length[$length]
or next;
printf "%6d %11d\n", $length, $occurrences;
}
A hash, while less efficient, could easily be used with next to no changes.
my %occurrences_by_length;
while (my $word = <>) {
chomp($word);
my $length = length($word);
++$occurrences_by_length{$length};
}
print "Length Occurrences\n";
for my $length (sort { $a <=> $b } keys(%occurrences_by_length)) {
my $occurrences = $occurrences_by_length{$length};
printf "%6d %11d\n", $length, $occurrences;
}

Related

function to find the longest increasing subsequence without arrays

I need to make a program in C with those instructions:
the problem is its kinda easy to do with arrays but without them,I cant think anything else.
we need to write a function that takes int n and then takes the sequences of numbers, the function need to return us the sum of the biggest sequence.
for n = 8 and the sequence = 2,4,6,8,11,14,17,9' (there is 2 long sequences of 4, (when 8 starts the new sequence and closes the current sequence) here we will get the print 50.`
for n = 2 and the sequence 7,19 the function will print 26.
for n = 8 and the sequence 8,6,4,2,11,14,15,16
the longest length is 4 so the function will print 20.
can't find any solution here without arrays
printf("please choose a n");
scanf("%d", &n);
y = n;
if (n == 1)
{
printf("Please enter the next number ");
scanf("%d", &series);
return series;
}
else
if (n == 2)
{
while (i < 2) {
{
printf("Please enter the next number ");
scanf("%d", &series);
sum += series;
}
return sum;
}
printf("Plase choose another number");
scanf("%d", &last);
sum += last;
printf("please choose another number");
scanf("%d", &series);
d = series - last;
sum = series + last;
bSum = sum;
for (i = 2; i < n; i++)
{
last = series;
printf("Please write another number; ");
scanf("%d", &series);
if (series - last == d)
{
sum += series;
count++;
}
if (series - last != d)
{
if (count > length)
{
bSum = sum;
length = count;
}
else if (count == length && sum > bSum)
{
bSum = sum;
}
}
}
I had coded this up, but needed clarification from OP (see my top comments).
As a result ...
I added code to pick a sequence of equal length to the best but with a larger sum.
Because it was minimal changes to my code, I chose that the longest sequence wins:
For input of -2 -1 1 2, this is a sequence of 4 with sum 0. It is not two sequences of 2 -2 -1 and 1 2 with sum 3.
For input of 1 1 1 1 1, this is a sequence of 5 with sum 5 and not 5 sequences of 1 [each] with sum 1
Here is the code. It is annotated:
#include <stdio.h>
int
main(void)
{
// get count of sequence numbers
int n;
scanf("%d",&n);
// length of current sequence
int curlen = 0;
// sum of current sequence
int cursum = 0;
// value of current number
int curval;
// value of previous number
int oldval;
// get the first number
scanf("%d",&oldval);
// add it to the current sequence
curlen += 1;
cursum += oldval;
// initialize the best sequence from the current one
int bestsum = cursum;
int bestlen = curlen;
// get all remaining numbers in the sequence
for (int i = 1; i < n; ++i, oldval = curval) {
// get new current number
if (scanf("%d",&curval) != 1) {
fprintf(stderr,"too short\n");
break;
}
// reset the current sequence if we're not increasing
if (curval < oldval) {
cursum = 0;
curlen = 0;
}
// add to the current sequence
cursum += curval;
curlen += 1;
// save off the longest sequence we've seen
if (curlen > bestlen) {
bestlen = curlen;
bestsum = cursum;
continue;
}
// save off a sequence that is the same length as the best but has a
// larger sum
if ((curlen == bestlen) && (cursum > bestsum)) {
bestsum = cursum;
continue;
}
}
// print the final/best sum
printf("%d\n",bestsum);
return 0;
}
Input:
4
-2 -1 1 2
Output: 0
Input:
5
1 1 1 1 1
Output: 5
Input:
2
7 19
Output: 26
Input:
8
2 4 6 8 11 14 17 9
Output: 62
Input:
8
8 6 4 2 11 14 15 16
Output: 58
Input:
12
1 2 3 4
2 3 4 5
3 4 5 6
Output: 18
Input:
2
2 1
Output: 2
Here is the perl script that I used to generate the tests:
Edit: Updated for example below.
#!/usr/bin/perl
# runseq -- run sequence program
#
# options:
# "-D" -- cc -D options
# "-S<sfile>" -- source file (DEFAULT: seq.c)
# "-clip" -- send output to xclip program
# "-d" -- debug runseq script
#
# arguments:
# 1-- [optional] comma separated list of sequence numbers (e.g. 3,7 2,1)
master(#ARGV);
exit(0);
# master -- master control
sub master
{
my(#argv) = #_;
optdcd(\#argv,
"+opt_D","opt_S","opt_clip");
# get source file
$sroot = $opt_S;
$sroot //= "seq";
$sroot =~ s/[.].+$//;
zprtx("compiling %s ...\n",$sroot);
$sfile = "$sroot.c";
sysfault("runseq: no source file\n")
unless (-e $sfile);
push(#cc,"cc");
push(#cc,"-o$sroot");
foreach $opt (#opt_D) {
push(#cc,"-D$opt");
}
push(#cc,$sfile);
$cmd = join(" ",#cc);
zprtx("%s\n",$cmd);
$code = vxsystem(#cc);
exit(1) if ($code);
$tmpfile = "inp.txt";
# send to xclip program
if ($opt_clip) {
open($xfout,"|xclip");
}
else {
$xfout = \*STDOUT;
}
{
# take tests from command line
if (#argv > 0) {
foreach $arg (#argv) {
$arg =~ s/,/ /g;
#body = ($arg);
dosum();
}
last;
}
# get test data from below
$xfdata = \*seq::DATA;
while ($dat = <$xfdata>) {
chomp($dat);
# ignore comments
next if ($dat =~ /^\s*#/);
# blank line separates tests
if ($dat =~ /^\s*$/) {
dosum();
next;
}
push(#body,$dat);
}
}
# do final test
dosum()
if (#body > 0);
close($xfout)
if ($opt_clip);
}
# optdcd -- decode command line options
sub optdcd
{
my(#syms) = #_;
my($argv);
my($arg);
my($ary);
my($symlhs,$symrhs,$val,$match);
$argv = shift(#syms);
# get options
while (#$argv > 0) {
$arg = $argv->[0];
last unless ($arg =~ /^-/);
shift(#$argv);
$match = 0;
foreach $symrhs (#syms) {
$symlhs = $symrhs;
$ary = ($symlhs =~ s/^[+]//);
$opt = $symlhs;
$opt =~ s/^opt_/-/;
zprt("optdcd: TRYARG arg='%s' symrhs='%s' symlhs='%s' opt='%s' ary=%d\n",
$arg,$symrhs,$symlhs,$opt,$ary);
if ($arg =~ /^$opt(.*)$/) {
$val = $1;
$val =~ s/^=//;
$val = 1
if ($val eq "");
if ($ary) {
zprt("optdcd: PUSH val=%s\n",$val);
push(#$symlhs,$val);
}
else {
zprt("optdcd: SET val=%s\n",$val);
$$symlhs = $val;
}
$match = 1;
last;
}
}
sysfault("optdcd: unknown option -- '%s'\n",$arg)
unless ($match);
}
}
sub dosum
{
my($bf);
my(#rhs);
my(#n);
zprtx("\n")
if ($opt_clip);
$xfdst = xfopen(">$tmpfile","dosum");
# get count of sequence values (i.e. "n")
foreach $bf (#body) {
#rhs = split(" ",$bf);
push(#n,#rhs);
}
printf($xfdst "%d\n",scalar(#n));
foreach $bf (#body) {
zprtx("dosum: %s\n",$bf)
if ($opt_clip);
print($xfdst $bf,"\n");
}
$xfdst = xfclose($xfdst);
# run program
my($outval) = (`./$sroot < $tmpfile`);
chomp($outval);
# output test input data and program output
outf("\n");
outf("----------\n")
if ($opt_clip);
outf("Input:\n");
outf("```\n")
if ($opt_clip);
$xfsrc = xfopen("<$tmpfile","dosum");
while ($buf = <$xfsrc>) {
outf("%s",$buf);
}
$xfsrc = xfclose($xfsrc);
outf("```\n")
if ($opt_clip);
outf("\n")
if ($opt_clip);
outf("Output: %d\n",$outval);
undef(#body);
}
sub outf
{
printf($xfout #_);
}
sub zprt
{
goto &zprtx
if ($opt_d);
}
sub zprtx
{
printf(STDERR #_);
}
sub sysfault
{
zprtx(#_);
exit(1);
}
sub xfopen
{
my($file,$who) = #_;
my($xf);
open($xf,$file) or
sysfault("xfopen: unable to open '%s' -- $!\n",$file);
$xf;
}
sub xfclose
{
my($xf) = #_;
close($xf);
undef($xf);
$xf;
}
sub vxsystem
{
my($cmd);
$cmd = join(" ",#_);
system($cmd);
$? >> 8;
}
package seq;
__DATA__
-2 -1 1 2
1 1 1 1 1
7 19
2 4 6 8 11 14 17 9
8 6 4 2 11 14 15 16
1 2 3 4
2 3 4 5
3 4 5 6
2 1
UPDATE:
Here is a version that adds some debug printing (-DDEBUG). And, selection of <= vs < (-DLE).
I've updated the runseq script above to handle this version [it is backwards compatible].
#include <stdio.h>
#if DEBUG
#define dbgprt(_fmt...) \
fprintf(stderr,_fmt)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
int
main(void)
{
// get count of sequence numbers
int n;
scanf("%d", &n);
// length of current sequence
int curlen = 0;
// sum of current sequence
int cursum = 0;
// value of current number
int curval;
// value of previous number
int oldval = 0;
// get the first number
scanf("%d", &curval);
// add it to the current sequence
curlen += 1;
cursum += curval;
dbgprt("OLD: oldval=%d curval=%d cursum=%d curlen=%d\n",
oldval,curval,cursum,curlen);
oldval = curval;
// initialize the best sequence from the current one
int bestsum = cursum;
int bestlen = curlen;
// get all remaining numbers in the sequence
for (int i = 1; i < n; ++i, oldval = curval) {
// get new current number
if (scanf("%d", &curval) != 1) {
fprintf(stderr, "too short\n");
break;
}
// reset the current sequence if we're not increasing
#if LE
if (curval <= oldval) {
cursum = 0;
curlen = 0;
dbgprt("ZAPLE\n");
}
#else
if (curval < oldval) {
cursum = 0;
curlen = 0;
dbgprt("ZAPLT\n");
}
#endif
// add to the current sequence
cursum += curval;
curlen += 1;
dbgprt("CUR: oldval=%d curval=%d cursum=%d curlen=%d\n",
oldval,curval,cursum,curlen);
// save off the longest sequence we've seen
if (curlen > bestlen) {
dbgprt("NEWLEN\n");
bestlen = curlen;
bestsum = cursum;
continue;
}
// save off a sequence that is the same length as the best but has a
// larger sum
if ((curlen == bestlen) && (cursum > bestsum)) {
dbgprt("NEWSUM\n");
bestsum = cursum;
continue;
}
}
// print the final/best sum
printf("%d\n", bestsum);
return 0;
}

Fixing MEL FOR loop

I have been stuck doing this for if loop for the last few days.
Basically, it's a loop that starts at the height of 6 and decreases within the FOR loop, when the height (y) hits 1 I want it to start increasing by 1.
At the moment the loop hits 1, the height increases by 1 but then because it loops back around it does not increase the next shape by another 1.
I know its a bit confusing but I hope I can grab some help!
Thanks!
int $f = 6;
for ($e = 24 ; $e <= 31; $e++)
{
if ($f <= 1) {
string $currentObject = $objects[$e];
select -r $currentObject ;
setAttr ($currentObject+".sy") ($f++);
}
else {
string $currentObject = $objects[$e];
select -r $currentObject ;
setAttr ($currentObject+".sy") ($f--);
}
}
If I understand your problem correctly, you only want to switch the direction of increment or decrement at the extremes, not between, i. e. you need another variable that codes the direction (incrementing or decrementing). I called that $diff in this code:
int $f = 6;
int $diff = -1;
for ($e = 24 ; $e <= 31; $e++)
{
if ($f <= 1) {
$diff = 1;
} else if ($f >= 6) {
$diff = -1;
}
string $currentObject = $objects[$e];
select -r $currentObject ;
setAttr ($currentObject+".sy") ($f);
$f = $f + $diff;
}

Longest pair of string elements or longest side of the pairs in Bash array

Given an array in Bash:
my_array=("a" "b" "a bc" "d" "ab" "cd")
Considering each two consecutive items in the array as a pair, how can I:
Get the length of the longest pair. In the example above it would be "a bc" "d", so length 5.
Get the length of the longest of the fist items in the pairs. Above would be "a bc", so length 4.
Get the length of the longest of the second items in the pairs. Above would be "cd", so length 2.
The trick is to use a for loop that increments an index variable by 2 every time. Everything else is basic parameter expansion to get the lengths of elements and tests against the current maximums.
#!/usr/bin/env bash
declare -a my_array=("a" "b" "a bc" "d" "ab" "cd")
declare -i maxlen_combo=0 maxlen_first=0 maxlen_second=0
IFS=""
for (( i = 0; i < ${#my_array[#]}; i += 2 )); do
if [[ $maxlen_first -lt ${#my_array[i]} ]]; then
maxlen_first=${#my_array[i]}
fi
if [[ $maxlen_second -lt ${#my_array[i+1]} ]]; then
maxlen_second=${#my_array[i+1]}
fi
combo="${my_array[*]:i:2}"
if [[ $maxlen_combo -lt ${#combo} ]]; then
maxlen_combo=${#combo}
fi
done
echo "Maximum pair length: $maxlen_combo"
echo "Maximum first length: $maxlen_first"
echo "Maximum second length: $maxlen_second"
You can use
printf "%s\n" ${my_array[#]} |
awk '{itemlen=length($0)}
NR%2==1 {
firstmaxlen = (itemlen > firstmaxlen) ? itemlen : firstmaxlen;
firsthalf=itemlen
}
NR%2==0 {
secondmaxlen = (itemlen > secondmaxlen) ? itemlen : secondmaxlen;
pairlen = (firsthalf + itemlen > pairlen ) ? firsthalf + itemlen : pairlen ;
}
END { printf ("maxcombo, maxfirst, maxsecond) = (%d, %d, %d)\n",
pairlen, firstmaxlen, secondmaxlen) }
'
In this case a small function can help:
printf "%s\n" ${my_array[#]} |
awk 'function max(a,b) { return (a>b ? a : b) }
{itemlen=length($0)}
NR%2==1 {
firstmaxlen = max(itemlen, firstmaxlen);
firsthalf=itemlen
}
NR%2==0 {
secondmaxlen = max(itemlen, secondmaxlen);
pairlen = max(firsthalf + itemlen, pairlen);
}
END { printf ("maxcombo, maxfirst, maxsecond) = (%d, %d, %d)\n",
pairlen, firstmaxlen, secondmaxlen) }
'

Build Lowest Number by Removing n digits from a given number

It takes as input a number(string) and then the task is to remove n numbers to give a resulting lowest possible number, but you have to take care of the order, that is the constraint. You cannot change the order of the original numbers.
I wanted it to work in O(n), so I did this:
#!/usr/bin/perl
#lowestNS.pl
#Date: 2016-06-28
use warnings;
use strict;
use utf8;
(#ARGV == 2) or die "2 args needed";
my $num = $ARGV[0];
my $d = $ARGV[1];
my #arr;
int($num) > 0 or die "Enter positive number";
print "Number in: $num\nDel: $d\n";
if(int($num) == 0) {
print "Result: 0\n";
exit;
}
else {
my $str = $num;
#arr = split(//, $str); #/Split on each number
#Split and multiply by reverse index, to give precedence to the order of numbers
for(my $i = 0; $i < #arr; $i++) {
$arr[$i] *= (#arr - $i);
}
}
print "arr: " . join(',' , #arr) . "\n";
for (my $j = 0; $j < $d; $j++) {
my $max = $arr[0];
my $m_index = -1;
#replace nth maximum with -1
for (my $i = 0; $i < #arr; $i++) {
if($max <= $arr[$i]) {
$max = $arr[$i];
$m_index = $i;
}
}
$arr[$m_index] = -1;
}
#return all numbers with value other than -1
my $result = "";
for (my $i = 0; $i < #arr; $i++) {
if($arr[$i] != -1){
$result = $result . "" . $arr[$i]/(#arr - $i);
}
}
print "Result: $result\n";
It works in all cases, except, cases like:
Number = 765028321
Delete = 5
The problem is the removal of 765028321 when it should have removed the 765028321.
because 2*5 > 3*3.
I think, the algorithm is straightforward:
Suppose, N is the number of digits to delete;
1. Find the first smallest digit in the first N digits, delete digits to the left of it, decrease N by the number of digits deleted.
2. if N>0 take the digits to the right and repeat the steps above.
Of course, we need to check for marginal cases, and to ensure the final number does not start with 0.
Here is the code draft:
#!/usr/bin/perl
use strict;
use warnings;
my ($number, $del)=#ARGV;
my #num = $number=~m{.}g;
my $curpos=0;
my #finalnum;
for(;;) {
last if $del <=0 || $curpos+$del>=#num;
my $minpos=$curpos;
for (my $i=$curpos;$i<$curpos+$del+1 && $i < #num;$i++) {
if ($num[$i]<$num[$minpos] && !($curpos==0 && $num[$i]==0)) {
$minpos=$i;
}
}
push #finalnum, $num[$minpos];
$del-=($minpos-$curpos);
$curpos=$minpos+1;
}
push #finalnum, #num[$curpos+$del..$#num] if $curpos+$del < #num;
print join '', #finalnum;
A possible solution can be:
Create an array having count of all digits, eg. in your case, this array will look like: 1,1,2,1,0,1,1,1,1,0
Then, in a greedy manner, remove all big digits, as many as can be done. So, you remove 9s, but since its count is 0, you still need to remove 5 digits. Remove 8s, so you still need to remove 4 digits and so on.
This case is kind of trivial, so you will be left with 2 2's, 1 1's and 1 0's. In there were, say 3 4's also in the number, you will then need to remove 1 4. So, you will remove the leftmost of partial removals required.
Its a greedy approach, and works in O(n).

display specific values form sorted array

this is my sorted array
$sortedArray = array($value_one_fin,$value_two_fin,$value_three_fin,$value_four_fin,$value_five_fin,$value_six_fin,$value_seven_fin,$value_eight_fin,$value_nine_fin,$value_ten_fin,$value_eleven_fin,$value_twelve_fin,$value_thirteen_fin,$value_fourteen_fin,$value_fifteen_fin,$value_sixteen_fin,$value_seventeen_fin,$value_eighteen_fin,$value_nineteen_fin,$value_twenty_fin,$value_twentyone_fin,$value_twentytwo_fin,$value_twentythree_fin,$value_twentyfour_fin);
$result=findClosest($sortedArray,$num1);
this is the variable where i store my entered number
$num1
what i want to do is to display values from sorted array in the manner Buy at / above: 102.51 Targets: 105,107.58,110.19,112.83
Stoploss : 100
in the above example i have entered 100 as $num1. as per calc the $result is also 100 so it has to display next greater closest number which is 102.51 and in stop loss it shuld display next smaller closest number
CONDITION
1>if entered number $num1 is greater than $result then display the next greater closest number from sorted array
2>if entered number $num1 is equal to $result then display the next greater closest number from sorted array
pls help me with this how can i do this im doing this
Buy At/Above ";if ($num1>$result) {echo "$result ";}if ($num1=$result) {echo "$result ";}if ($num1<$result) {echo "$result ";}echo "for Targets"; if ($result<$value_one_fin){echo " $value_one_fin,";}
if ($result<$value_two_fin){echo " $value_two_fin,";}
if ($result<$value_three_fin){echo " $value_three_fin,";}
if ($result<$value_four_fin){echo " $value_four_fin,";}
if ($result<$value_five_fin){echo " $value_five_fin,";}
if ($result<$value_six_fin){echo " $value_six_fin,";}
if ($result<$value_seven_fin){echo " $value_seven_fin,";}
if ($result<$value_eight_fin){echo " $value_eight_fin,";}
if ($result<$value_nine_fin){echo " $value_nine_fin,";}
if ($result<$value_ten_fin){echo " $value_ten_fin,";}
if ($result<$value_eleven_fin){echo " $value_eleven_fin,";}
if ($result<$value_twelve_fin){echo " $value_twelve_fin,";}
if ($result<$value_thirteen_fin){echo " $value_thirteen_fin,";}
if ($result<$value_fourteen_fin){echo " $value_fourteen_fin,";}
if ($result<$value_fifteen_fin){echo " $value_fifteen_fin,";}
if ($result<$value_sixteen_fin){echo " $value_sixteen_fin,";}
if ($result<$value_seventeen_fin){echo " $value_seventeen_fin,";}
if ($result<$value_eighteen_fin){echo " $value_eighteen_fin,";}
if ($result<$value_nineteen_fin){echo " $value_nineteen_fin,";}
if ($result<$value_twenty_fin){echo " $value_twenty_fin,";}
if ($result<$value_twentyone_fin){echo " $value_twentyone_fin,";}
if ($result<$value_twentytwo_fin){echo " $value_twentytwo_fin,";}
if ($result<$value_twentythree_fin){echo " $value_twentythree_fin,";}
if ($result<$value_twentyfour_fin){echo " $value_twentyfour_fin";}
but its not working and also how to display STOP LOSS
Apologies if I've misunderstood the question - I'm not 100% clear what you're trying to achieve, but I'll try to be helpful.
It looks as though your array is sorted smallest to largest. So to find the first value greater than or equal to $num1, simply loop through your array from beginning to end, and stop when you find a value >= $num1 e.g.
function findClosest($sortedArray, $num1) {
foreach ($sortedArray as $value) {
if ($value >= $num1) {
return $value;
}
}
}
To find the closest number less than or equal to $num1, loop through the array from the end to the beginning e.g.
function findClosestLessThanOrEqual($sortedArray, $num1) {
for ($i = sizeOf($sortedArray) - 1; $i >= 0; $i--) {
if ($sortedArray[$i] <= $num1) {
return $sortedArray[$i];
}
}
}
There are faster ways to search a large array, but if your array only contains 24 values I think that's the simplest way.
Just needs a slight tweak to get the first 5 items >= $num1:
function find5Closest($sortedArray, $num1) {
for ($i = 0; $i < sizeOf($sortedArray); $i++) {
if ($sortedArray[$i] >= $num1) {
return array_slice($sortedArray, $i, 5);
}
}
}
This should return an array up to 5 values. If there are only 3 values >= $num1, then it will only return 3 values; if no values are >= $num1 it will return an empty array.
Similarly:
function find5ClosestLessThanOrEqual($sortedArray, $num1) {
for ($i = sizeOf($sortedArray) - 1; $i >= 0; $i--) {
if ($sortedArray[$i] <= $num1) {
return array_slice($sortedArray, min(0, $i-4), 5);
}
}
}

Resources