Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
$a = gc "$home\documents\textfile.txt"
for ($i = 0; $i -lt $a.Count + 1; $i++)
{
$a[($a.count-1) - $i]
}
I know the first line gets the content of the text file, and the file is shown from bottom to top, but what exactly is the for loop doing step-by-step?
The for loop sets $i to 0, loops for as long as it's less than or equal to $a.Count-1 and increments after each iteration.
$a.Count is the total number of lines in the file, so $a.Count-1 is the final element (line) of $a which is an array indexed from zero.
Within each iteration $i gets larger and so the line inside the loop returns the value held at the last element minus $i.
If $a has 5 lines for example, then there are 5 elements in $a. These are $a[0] to $a[4]
On the first iteration $i is zero so the value within the square brackets evaluates to 5-1-0 = 4. This gives the last line.
On the second iteration all that changes is $i is incremented. This evaluates to 5-1-1 = 3. This is the second to last line.
This carries on until $i = 4 (which is $a.Count-1) and the code evaluates 5-1-4=0, which results in the first element of $a being output.
Hope that makes sense enough for you...
What the for loop is doing is getting the index of the string provided by $a.
You can play with indexing by setting a variable equal to a string or array, and looking at each of the indices within it. Take, for example, $x = "this is a string". You can then look at $x[n] where n is an integer, and it will show you what that index in the string or array contains. For $x[0] Powershell would return "t", or if $x were an array of strings it would return "this".
Now that we know what indexing is, we can look at the math operators within your for loop. To think about this in the way that Powershell is processing it, we'll work from the parenthesis out. $a.count returns a value equal to the number of objects contained in $a. If we look at what Get-Content returns (with help from our handy Get-Help gc), we can see that it is returning an object per line contained in that file.
When we apply these principles to the script you provided, we can determine that the script will return the lines of the file in reverse order.
Related
I have following code:
foreach ($arg in $args) {
Write-Host "Arg: $arg";
$param1=$args[0]
}
Write-host "Number of args: " $args.Length
write-host Last Arg is: "$($args.count)"
I get this, when I run it:
./print_last_arg.ps1 a b c
Arg: a
Arg: b
Arg: c
Number of args: 3
Last Arg is: 3
What I would like to have is name of last argument, so:
Last Arg is: 3
should be:
Last Arg is: c
Sorry for such a stupid question but I am totally begginer in PS and cannot google the result...
PowerShell supports negative indices to refer to elements from the end of a collection, starting with -1 to refer to the last element, -2 to the penultimate (second to last) one, and so on.
Therefore, use $args[-1] to refer to the last argument passed.
For more information, see the conceptual about_Arrays help topic.
Note that you can also use the results of expressions as indices; e.g., the equivalent of $args[-1] is $args[$args.Count-1] (assuming the array has at least one element).
Additionally, you may specify multiple indices to extract a sub-array of arbitrary elements. E.g., $args[0, -1] returns a (new) array comprising the input array's first and the last element (assuming the array has at least two elements).
.., the range operator is particularly useful for extracting a range of contiguous elements. E.g., $args[0..2] returns a (new) array comprising the first 3 elements (the elements with indices 0, 1, and 2).
You can even combine individual indices with ranges, courtesy of PowerShell's + operator performing (flat) array concatenation.
E.g., $args[0..2 + -1] extracts the first 3 elements as well as the last (assumes at least 4 elements).
Note: For syntactic reasons, if a single index comes first in the index expression, you need to make it an array with the unary form of , the array constructor operator, to make sure that + performs array concatention; e.g., $args[,-1 + 0..2] extracts the last element followed by the first 3.
Pitfall: Combining a positive .. start point with a negative end point for up-to-the-last-Nth-element logic does not work as intended:
Assume the following array:
$a = 'first', 'middle1', 'middle2', 'last'
It is tempting to use range expression 1..-2 to extract all elements "in the middle", i.e. starting with the 2nd and up to the penultimate element - but this does not work as expected:
# BROKEN attempt to extract 'middle1', 'middle2'
PS> $a[1..-2]
middle1
first
last
middle2
The reason is that 1..-2, as a purely arithmetic range expression, expanded to the following array (whose elements happen to be used as indices into another array): 1, 0, -1, -2. And it is these elements that were extracted: the 2nd, the first, the last, the penultimate.
To avoid this problem, you need to know the array's element count ahead of time, and use an expression to specify the end of the range as a positive number:
# OK: extract 'middle1', 'middle2'
# Note that the verbosity and the need to know $a's element count.
PS> $a[1..($a.Count-2)]
middle1
middle2
Unfortunately, this is both verbose and inconvenient, especially given that you may want to operate on a collection whose count you do not know in advance.
GitHub issue #7940 proposes a future enhancement to better support this use case with new syntax, analogous to C#'s indices-and-ranges feature, so that the above could be written more conveniently with syntax such as $a[1..^1]
I'm completely lost on some homework. The assignment is to use a while loop and prompt the user to enter 5 numbers. My current code looks like the following:
$x = 1
do
{
Write-Host 'Enter 5 numbers'
$x++
} while ($x -eq 5)
Seems like issue is somewhere in here ($x -eq "5"). I want PowerShell just to prompt the user for 5 random numbers then get the sum of those numbers.
A homework assignment. 8^}
Oh well... Here is one way to do this.
Yet, taking your original post and what you show in your comment. Your issue is, you are not capturing anything to sum up, nor are you using a counter to meet you entry limit.
Clear-Host
# Initial variables
$ResponseCount = 0
$Total = 0
do {
# Get a user response
$response = Read-Host "Enter 5 numbers"
# increment response counts
$ResponseCount ++
# capture and sum the entered numbers
$Total += $response
}
until ($ResponseCount -eq "5")
"`nYou have completed the required 5 entries"
"The sum of the entered numbers is: $Total"
Enter 5 numbers: 10
Enter 5 numbers: 20
Enter 5 numbers: 30
Enter 5 numbers: 40
Enter 5 numbers: 50
You have completed the required 5 entries
The sum of the entered numbers is: 150
You're correct that the issue is your condition for the loop. Currently you're telling PowerShell to run the loop while $x is exactly five. If you want your loop to properly work you will have to change the condition. You can check the PowerShell help for about_Comparison_Operators to get an idea about the operators that are available.
The other thing you need to do is read the actual values and save whatever the user enters to build the sum. There are quite a few approaches to this and the other answer has a solution for that. Also consider using more expressive variable names to make it easier for someone else to follow your code (though that's not so much an issue for this short example).
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Improve this question
print $some_array[$#some_array - 1];
My intuitive understanding is the above code should print the 2nd last element in #some_array.
However in reality it does not. It prints something I cannot made sense of.
It does print the second to last element in the array:
my #nums = qw(a b c d e);
print "$nums[$#nums-1]\n"; # prints 'd'
Here, $#nums gives the last index of an array (in this case 4 - arrays are zero-based), and hence $#nums - 1 gives array element 3.
A more common way of writing this would be:
$array[-2]
The behavior of $some_array[$#some_array - 1] depends on how many elements #some_array has.
If #some_array has 2 or more elements (scalar #array >= 2, or equivalently $#array >= 1), then your statement:
print $some_array[$#some_array - 1];
should work correctly. Say the array has 10 elements, then $#some_array == 9, and you'll print $some_array[8], the second-to-last element.
If #some_array has just one element, then $#some_array == 0, and you'll print the "-1th" element. Negative array indices are treated specially: -1 gives you the last element of the array, -2 the second-to-last, and so forth. And if your goal is to print the second-to-last element of the array, you should be using this feature.
If #some_array is empty (scalar #some_array == 0, $#some_array == -1), then your statement will try to print $some_array[-2], which doesn't exist, resulting in an error message.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have an array in the third element dataArr[2], I know it contains a 10 digit phone. I need to only read or print the first 6 digits. For instance if my phone is 8329001111, I need to print out the 832900. I tried to see if I can use substr but I keep reading or printing the full list. Do I need to dereference..
Try this :
$dataArr[2] =~ s/\s//g; # ensure there's no spaces
print substr($dataArr[2], 0, 6);
# ^ ^ ^
# variable | |
# offset start|
# |
# substring length
I need an algorithm that identifies all possible combinations of a set of numbers that sum to some other number.
For example, given the set {2,3,4,7}, I need to know all possible subsets that sum to x. If x == 12, the answer is {2,3,7}; if x ==7 the answer is {{3,4},{7}} (ie, two possible answers); and if x==8 there is no answer. Note that, as these example imply, numbers in the set cannot be reused.
This question was asked on this site a couple years ago but the answer is in C# and I need to do it in Perl and don't know enough to translate the answer.
I know that this problem is hard (see other post for discussion), but I just need a brute-force solution because I am dealing with fairly small sets.
sub Solve
{
my ($goal, $elements) = #_;
# For extra speed, you can remove this next line
# if #$elements is guaranteed to be already sorted:
$elements = [ sort { $a <=> $b } #$elements ];
my (#results, $RecursiveSolve, $nextValue);
$RecursiveSolve = sub {
my ($currentGoal, $included, $index) = #_;
for ( ; $index < #$elements; ++$index) {
$nextValue = $elements->[$index];
# Since elements are sorted, there's no point in trying a
# non-final element unless it's less than goal/2:
if ($currentGoal > 2 * $nextValue) {
$RecursiveSolve->($currentGoal - $nextValue,
[ #$included, $nextValue ],
$index + 1);
} else {
push #results, [ #$included, $nextValue ]
if $currentGoal == $nextValue;
return if $nextValue >= $currentGoal;
}
} # end for
}; # end $RecursiveSolve
$RecursiveSolve->($goal, [], 0);
undef $RecursiveSolve; # Avoid memory leak from circular reference
return #results;
} # end Solve
my #results = Solve(7, [2,3,4,7]);
print "#$_\n" for #results;
This started as a fairly direct translation of the C# version from the question you linked, but I simplified it a bit (and now a bit more, and also removed some unnecessary variable allocations, added some optimizations based on the list of elements being sorted, and rearranged the conditions to be slightly more efficient).
I've also now added another significant optimization. When considering whether to try using an element that doesn't complete the sum, there's no point if the element is greater than or equal to half the current goal. (The next number we add will be even bigger.) Depending on the set you're trying, this can short-circuit quite a bit more. (You could also try adding the next element instead of multiplying by 2, but then you have to worry about running off the end of the list.)
The rough algorithm is as follows:
have a "solve" function that takes in a list of numbers already included and a list of those not yet included.
This function will loop through all the numbers not yet included.
If adding that number in hits the goal then record that set of numbers and move on,
if it is less than the target recursively call the function with the included/exluded lists modified with the number you are looking at.
else just go to the next step in the loop (since if you are over there is no point trying to add more numbers unless you allow negative ones)
You call this function initially with your included list empty and your yet to be included list with your full list of numbers.
There are optimisations you can do with this such as passing the sum around rather than recalculating each time. Also if you sort your list initially you can do optimisations based on the fact that if adding number k in the list makes you go over target then adding k+1 will also send you over target.
Hopefully that will give you a good enough start. My perl is unfortuantely quite rusty.
Pretty much though this is a brute force algorithm with a few shortcuts in it so its never going to be that efficient.
You can make use of the Data::PowerSet module which generates all subsets of a list of elements:
Use Algorithm::Combinatorics. That way, you can decide ahead of time what size subsets you want to consider and keep memory use to a minimum. Apply some heuristics to return early.
#!/usr/bin/perl
use strict; use warnings;
use List::Util qw( sum );
use Algorithm::Combinatorics qw( combinations );
my #x = (1 .. 10);
my $target_sum = 12;
{
use integer;
for my $n ( 1 .. #x ) {
my $iter = combinations(\#x, $n);
while ( my $set = $iter->next ) {
print "#$set\n" if $target_sum == sum #$set;
}
}
}
The numbers do blow up fairly rapidly: It would take thousands of days to go through all subsets of a 40 element set. So, you should decide on the interesting sizes of subsets.
Is this a 'do my homework for me' question?
To do this deterministically would need an algorithm of order N! (i.e. (N-0) * (N-1) * (N-2)...) which is going to be very slow with large sets of inputs. But the algorithm is very simple: work out each possible sequence of the inputs in the set and try adding up the inputs in the sequence. If at any point the sum matches, you've got one of the answers, save the result and move on to the next sequence. If at any point the sum is greater than the target, abandon the current sequence and move on to the next.
You could optimize this a little by deleting any of the inputs greater than the target. Another approach for optimization would be to to take the first input I in the sequence and create a new sequence S1, deduct I from the target T to get a new target T1, then check if T exists in S1, if it does then you've got a match, otherwise repeat the process with S1 and T1. The order is still N! though.
If you needed to do this with a very large set of numbers then I'd suggest reading up on genetic algorithms.
C.
Someone posted a similar question a while ago and another person showed a neat shell trick to answer it. Here is a shell technique, but I don't think it is as neat a solution as the one I saw before (so I'm not taking credit for this approach). It's cute because it takes advantage of shell expansion:
for i in 0{,+2}{,+3}{,+4}{,+7}; do
y=$(( $i )); # evaluate expression
if [ $y -eq 7 ]; then
echo $i = $y;
fi;
done
Outputs:
0+7 = 7
0+3+4 = 7