Persistence of a number

The persistence of a number $$\x = d_1d_2d_3...d_n\$$, with $$\d_1 \ne 0\$$, under some function $$\f : \mathbb N_0 \times \mathbb N_0 \to \mathbb N_0\$$ is defined as the number of applications of $$\f\$$ to the digits of $$\x\$$ before it reaches a single digit integer. That is, if we have the map

$$I_f: (x = d_1d_2d_3...d_n) \mapsto f(f(...f(d_1, d_2), d_3), ...), d_n),$$

the persistence is defined as

$$P_f(x) = \begin{cases} 0, & x \in \{0,1,2,3,4,5,6,7,8,9\} \\ 1 + P_f(I_f(x)), & \text{otherwise} \end{cases}$$

Some examples include:

• Additive persistence: $$\P_+(2718) = 2\$$, $$\P_+(5) = 0\$$ and $$\P_+(2677889) = 3\$$
• Multiplicative persistence: $$\P_\times(68889) = 7\$$, $$\P_\times(25) = 2\$$ and $$\P_\times(8) = 0\$$
• Minimal/maximal digit: $$\P_{\min}(1734) = 1\$$, $$\P_{\max}(48203) = 1\$$ and $$\P_{\min}(5) = 0\$$

Given a blackbox function $$\f : \mathbb N_0 \times \mathbb N_0 \to \mathbb N_0\$$ and a positive integer $$\x = d_1d_2d_3...d_n\$$, where $$\d_i\$$ are digits with $$\d_1 \ne 0\$$, output the persistence of $$\x\$$ under $$\f\$$.

You may assume that $$\f\$$ will eventually reach a single digit number when repeatedly applied to $$\x\$$'s digits.

This is a challenge, so shortest code wins.

Test cases

f(x, y), x -> output
x + y, 2677889 -> 3
x × y, 68889 -> 7
x ** y, 29 -> 4
φ(x × y), 736 -> 2                 (φ is the Euler Totient function)
x/2 + (x × y), 1234567 -> 5        (using floor division)
|x - y|, 9 -> 0

• Related. Related. Brownie points for beating/matching my 7 byte Jelly answer Sep 5 at 0:04
• what is Euler Totient function?
– tsh
Sep 5 at 1:30
• Sep 5 at 1:31

K (ngn/k), 14 bytes

{#1_(x/.'$:)\}  Try it online! Uses .'$: instead of 10\ because the latter gives () for 0 (and */ of () is 1), breaking the (*, 68889) test case.

{#1_(x/.'$:)\} Curried function, 1st arg: f, 2nd arg: n ( )\ repeat and collect starting from n until convergence: .'$:         digits
x/             reduce by x
#1_              length minus 1


Python, 70 71 bytes

p=lambda f,n:n>9and-~p(f,(i:=lambda n:f(i(n//10),n%10)if n>9else n)(n))


Attempt This Online!

Takes in an anonymous function f and an integer n.

+1 bytes from @xnor for noticing an error when n=0 in i.

Python, 65 bytes

p=lambda f,n:~-len(n)and-~p(f,reduce(f,n))
from functools import*


Attempt This Online!

Takes in an anonymous function f and a string n representing an integer.

Python 2, 42 bytes

p=lambda f,n:~-len(n)and-~p(f,reduce(f,n))


Attempt This Online!

Same as above. Posted based on the recommendation of @tsh.

• You can remove the from functools import* by switching to Python 2.
– tsh
Sep 5 at 2:24
• Man, I thought you were some new user I'd never seen before, but then I spelled out the character codes and it matched the pfp lol Sep 5 at 3:23
• It looks like the and/or in your first solution catches a hitch when getting n=0: TIO. You can fall back to an if/else for +1 byte; maybe you see better.
– xnor
Sep 5 at 21:14

Vyxal, 6 bytes

£‡¥RİL


Try it Online!

Takes inputs from the stack due to technical limits.

£      # Store function to register
‡--   # Next two elements as a function...
R   # Reduce by (implicit digits)...
¥    # The function stored in the register
İ  # Apply that function until the result doesn't change, not including the initial value
L # Count the number of different values.

• Try it Online! for 5 bytes by setting the input in the header. I think it should be valid Sep 5 at 0:41
• @lyxal I'd say it isn't really, since it's not something you can do normally. Sep 5 at 0:45
• It's the equivalent of putting a function into the inputs though. It's setting STDIN. I'll ask in chat Sep 5 at 0:46

PARI/GP, 38 bytes

g->p(n)=if(n>9,1+p(fold(g,digits(n))))


Attempt This Online!

A curried function that takes input as (f)(x).

Jelly, 7 bytes

Dç/$ƬL’  Try it online! Takes a function from the previous line and a number from an argument. Dç/$       Convert an integer to its decimal digits and reduce by the input function
Ƭ      Repeat and collect the results until the results are not unique
(repeats after reaching a single digit number)
L’    Get its length minus 1


05AB1E, 12 10 bytes

ΔÅ»X.V}θ}N


Assumes the function is defined as a string in variable X.

Explanation:

Δ       # Apply until the result no longer changes,
# using the (implicit) input-integer in the first iteration:
Å»     #  Reduce the digits of the integer by, keeping all intermediate results:
X    #   Push function-string X
.V  #   Evaluate and execute it as 05AB1E code
}θ    #  After the reduce-by: only keep the resulting last value
}N      # After the until-no-more-changes-loop: push the last 0-based index of the loop
# (which is output implicitly as result)


Unfortunately, 05AB1E lacks functions, so we'll have to store the 'black box function' in a string and execute it as 05AB1E code with .V.
In addition, 05AB1E's regular reduce-by builtin .» only works with single builtins (e.g. .»*), so we'll have to use the reduce-by that keeps intermediate results and then just keep the final result, in order to have a larger body with multiple builtins (pushing X, and executing as 05AB1E .V in this case). (e.g. Å»*}θ).

f#n=sum[1+f#foldl1 f[read[x]|x<-show n]|n>9]


Try it online!

• saved 3 Bytes thanks to @xnor

# is an infix function taking the function to apply as f and a integer number as n.

Uses the trick shorter conditionals when one outcome is the empty list combined with a recursive approach by @xnor idea.

If n > 9 yeld 1+ result of # with number transformed, the result is the sum.

• 44 bytes
– xnor
Sep 5 at 21:04

Japt, 11 bytes

Black-box function is pre-assigned to variable V, although functions that consist of a single basic operation can be passed via input variable V.

@=ìrV)¨A}fÄ


Try it

Prolog (SWI), 90 bytes

X/Y/Z:-Y<10,Z=0;number_codes(Y,A),maplist(plus(48),[F|B],A),foldl(X,B,F,S),X/S/H,Z is H+1.


Try it online!

90 bytes by Steffan.

• You can use , instead of ->. It will provide multiple choicepoints, but that's fine: Try it online! Sep 7 at 16:15
• 90 bytes using operators instead of functions: Try it online! Sep 7 at 16:16

Ruby, 47 bytes

->g,n{n>9?1+f[g,n.digits.reverse.reduce(&g)]:0}


Try it online!

JavaScript (ES6), 48 bytes

Expects (F)(n), where n is passed as a string. Returns false for 0.

F=>g=n=>n>9&&g([...n].map(x=>+x).reduce(F)+'')+1


Try it online!

Burlesque, 19 bytes

{XXr[}j1ia1jC~qlnfi


Try it online!

{XX    # As digits
r[    # Reduce by F
}j1ia  # Insert F before reduce
1jC~   # Evaluate infinitely
qlnfi  # Find first single digit index


BQN, 25 bytes

{𝕩>9?1+𝕊𝔽˜´⌽'0'-˜•Fmt𝕩;0}


Defines a 1-modifier that can be applied to a dyadic function F, turning it into a monadic function that takes a single number and returns its persistence under F. Try it at BQN online!

Explanation

Here 𝔽 is the original dyadic function, 𝕊 is the modified function, and 𝕩 is the single argument of the modified function.

{𝕩>9?1+𝕊𝔽˜´⌽'0'-˜•Fmt𝕩;0}
{                        }  Block (which is a 1-modifier due to the presence of 𝔽 in it)
𝕩>9                         Does the argument have multiple digits?
?                        If so (recursive case):
•Fmt𝕩       Format the argument as a string
'0'-˜            Subtract '0' from each char, giving a list of digits
⌽                  Reverse
´                   Right fold on:
𝔽˜                    𝔽 with reversed arguments
𝕊                      Recurse on that value
1+                       Add one to the result of the recursive call
;0    Otherwise (base case), return 0


Unfortunately, BQN doesn't have a left-fold modifier. Scan works left-to-right, but then it costs two bytes to extract the last value from the result list.

tinylisp, 66 bytes

(load library
(d P(q((F N)(i(l N 10)0(a 1(P F(foldl F(to-base 10 N


Defines a function P that takes a function F and a number N and returns the persistence of N under F. Try it online!

Explanation

(load library


The library contains definitions for foldl and to-base.

(d P(q((F N)


Define P to be a function of two arguments, F and N.

(i(l N 10)0


If N is less than 10, return 0. Otherwise...

(a 1(P F


Add 1 to the result of calling P recursively on F and the result of...

(foldl F(to-base 10 N


Convert N to its base-10 digits and left-fold on F.