Abbas
Abbas

Reputation: 4069

Python structural pattern matching for string containing float

How can I use structural pattern matching for the following use case:

values = ["done 0.0", "done 3.9", "failed system busy"]

for v in values:
   vms = v.split()
   match vms:
       case ['done', float()>0]: # Syntax error
           print("Well done")
       case ['done', float()==0]: # Syntax error
           print("It is okay")
       case ['failed', *rest]:
           print(v)

Please excuse me for the syntax errors, I have written this to demonstrate my thought process.

What could be the right syntax to achieve this pattern matching? Is it even possible?

Upvotes: 1

Views: 132

Answers (2)

Nuno André
Nuno André

Reputation: 5379

Almost got it. You missed referencing the second value.

values = ['done 0.0', 'done 3.9', 'failed system busy']

for v in values:

   match v.split():
       case 'done', x if float(x) > 0:
           print('Well done')
       case 'done', x if float(x) == 0:
           print('It is okay')
       case 'failed', *rest:
           print(v)

Also, you may want to add a last case that catches the not covered ones:

       case _:
           print('Any other case')

This is clearer and more concise than the ìf-then-else-try-catch alternative. Actually, match was created just for cases like this one. That's why it's called structural pattern matching.

Upvotes: 1

user19077881
user19077881

Reputation: 5450

if ...else would be simpler but if you do want pattern matching then you have a number of issues to be resolved. Your string does not contain a float, it contains a string of characters which could be converted to a float. So to test value of a float you have to test that it IS a float in string form and then convert to a float then test. The 'wildcard' character is _ which should be used to capture non-matching elements with *_ to catch any number of other elements. The following code does what I think you want and could be the basis for further development. Firstly Regex is used to test for the float with a 'guard' expression in the pattern. The Regex would pick up error entries such as "3.x".

import re
values = ["done 0.0", "done 3.9", "failed system busy"]

for v in values:
    vms = v.split()
 
    match vms:
        case ['done', x] if x == '0.0': print(vms, 'It is OK')
        case ['done', x] if re.match(r'\d+.\d+', x) and float(x) > 3.0: print(vms, 'Well done')
        case ['failed', *_]: print(v)
        case _: print('unknown case')

produces:

['done', '0.0'] It is OK
['done', '3.9'] Well done
failed system busy

Another way, without Regex, would be to write a checking function such as:

def check_float(f):
    f = f.strip()
    try:
        _ = float(f)
    except:
        return False
    return True

and then the case would be:

case ['done', x] if check_float(x) and float(x) > 3.0: print(vms, 'Well done')

Upvotes: 1

Related Questions