Named patterns as function argument descriptors

I have a function timeToMinutes that takes a 24-hour time as a string argument (in digital clock format, e.g., "15:47") and converts it to the number of minutes past the last midnight. For instance, timeToMinutes["3:45"] yields 225.

Here is the function as I have written it:

timeToMinutes[time_] :=
Function[pair, 60*pair[[1]] + pair[[2]]]@(ToExpression /@ time~StringSplit~":");


First it splits the string at the colon, then converts the two resultant strings to a pair of expressions, then sums 60 times the first member of the pair and one times the second member of the pair. This works fine, but I would like to rewrite it using pattern matching. What I have in mind is something like:

newTimeToMinutes = Function[time:(ToString[hours_]~~":"~~ToString[mins_]), 60*hours + mins]


The argument time would be dissected into the two numbers on either side of the colon, furthermore, the numbers on either side of the colon would be used in their numerical form instead of their string form (this is why I would like it if I could use ToString[hours_] to recognize something like "45" as the number 45.).

Is this possible? It seems like my problem is that patterns can be used in the arguments for arbitrary functions so long as they're patterns on expression input. On the other hand, the built-in string pattern tools that can pattern match on strings like StringReplace expect to take a string in and return a string out.

-
– Mr.Wizard Mar 16 '13 at 7:02

String is an atomic object, which means that you can't deconstruct it like you propose, just like you can't expect f[a_b_c_] to match f[123]. You will have to use functions that specifically operate on string objects to deconstruct them.

You can rewrite your function using string patterns as:

f[time_] := StringCases[time, hrs__ ~~ ":" ~~ mins__ :> {60, 1}.ToExpression@{hrs, mins}]

-
You beat me this time. :-) – Mr.Wizard Mar 16 '13 at 5:15

Here is an alternative definition that's a little shorter:

newTime[t_] := FromDigits /@ StringSplit[t, ":"].{60, 1}

newTime["14:30"]

(* ==> 870 *)


And to come back to the question of patterns in the function argument: you can certainly use patterns to restrict the type of arguments to which this definition applies. Here I'll combine two things: a test for String arguments, and an additional condition to make sure there's a colon in the string in the first place:

Clear[newTime]

newTime[t_String] /;
StringMatchQ[t, __ ~~ ":" ~~ __] := (FromDigits /@ StringSplit[t, ":"]).{60, 1}

newTime["12:22"]

(* ==> 742 *)

-
I recommend t_String over t_?StringQ as it's faster and simpler. (+1) – Mr.Wizard Mar 16 '13 at 6:44
Your use of condition is not what the OP intended though. The OP wanted to do something like the equivalent of f[{a_,b_}] := ... for f[{1,2}] for strings i.e., deconstruct it in place – R. M. Mar 16 '13 at 14:49
@Mr.Wizard Right, I changed to _String. – Jens Mar 16 '13 at 15:24
@rm-rf Sure, and as you said it can't be done. But I implemented what I think the OP originally had in mind. – Jens Mar 16 '13 at 15:27