Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return not behaving as i expect

I'm solving the problem

here is the solution i made first

using namespace System.Linq
$DebugPreference = "Continue"
 
class kata{
    static [string] FirstNonRepeatingLetter ([String]$string_){
        [Enumerable]::GroupBy([char[]]$string_, [func[char, char]]{$args[0]}).Foreach{
            if ($_.Count -eq 1){
                Write-Debug ($_.key)
                return $_.Key 
            }
        }
        return "~~~"
    }
}
 
Write-Debug ([kata]::FirstNonRepeatingLetter("stress"))

it's return

DEBUG: t
DEBUG: r
DEBUG: e
DEBUG: ~~~

this is not what i expected I try this

using namespace System.Linq
$DebugPreference = "Continue"
 
class kata{
    static [string] FirstNonRepeatingLetter ([String]$string_){
        $groups = [Enumerable]::GroupBy([char[]]$string_, [func[char, char]]{$args[0]})
        foreach ($group in $groups)
        {
            if ($group.Count -eq 1){
                return $group.Key 
            }
        }
        return "~~~"
    }
}
 
Write-Debug ([kata]::FirstNonRepeatingLetter("stress"))

and then what I wanted to see

DEBUG: t

and now I don't understand why these codes work differently...

like image 788
Алексей Семенов Avatar asked Apr 13 '26 21:04

Алексей Семенов


1 Answers

You're executing return inside a script block ({ ... }) you've passed to the .ForEach() array method method:

  • In script blocks passed as arguments (as opposed to blocks that are integral to statements such as if and foreach), return exits only the block itself.

  • Therefore, your .ForEach() enumeration continues after each return; that is, while execution inside the script block is terminated, execution continues with the next .ForEach() iteration.

There actually is no direct way to stop the enumeration of .ForEach() (by contrast, the related .Where() array method has an optional parameter that accepts a 'First' to stop enumerating after finding the first match).

You have two options:

  • Set a Boolean flag inside your script block to indicate whether a result has been found, and exit the script block right away if the flag is found to be set.

    • The obvious downside to this is that the enumeration always runs to completion.
  • Use a dummy loop so that you can repurpose a break statement to break out of the loop and thereby also out of the .ForEach() enumeration.

    • Note: Without the dummy loop, break would be looking up the entire call stack for a loop to break out of; if it finds none, execution would terminate overall - see this answer for more information.
  • Either way, you need a return statement outside to script block in order to return a value from your class method.

Here's how to implement the dummy-loop approach:

using namespace System.Linq
$DebugPreference = 'Continue'
 
class kata{
    static [string] FirstNonRepeatingLetter ([String]$string_){
      $result =  '~~~'
      do {  # dummy loop
        [Enumerable]::GroupBy([char[]]$string_, [func[char, char]]{$args[0]}).Foreach{
          if ($_.Count -eq 1){
                Write-Debug ($_.key)
                $result = $_.Key
                break  # break out of the dummy loop
            }
        }
      } while ($false)
      return $result
    }
}
 
Write-Debug ([kata]::FirstNonRepeatingLetter('stress'))
like image 182
mklement0 Avatar answered Apr 15 '26 20:04

mklement0