Zulip Chat Archive
Stream: new members
Topic: Advice on writing idiomatic Lean4
Will Bradley (Apr 27 2024 at 23:36):
How close are each of these small programs to idiomatic Lean? One I wrote like I would in C (imperatively), the other functionally. Thanks so much
Program 1:
-- count six-digit numbers whose digits sum to 10
def main : IO Unit := do
let mut count := 0
for n in [100000 : 1000000] do
let mut n' := n
let mut sum := 0
while n' ≠ 0 do
sum := sum + n' % 10
if sum > 10 then break
n' := n' / 10
if sum = 10 then
count := count + 1
IO.println count
Program 2:
-- [start, stop)
def List.range2 (start stop: Nat): List Nat :=
List.range (stop - start) |>.map (. + start)
-- count six-digit numbers whose digits sum to 10
def main : IO Unit :=
let answer := List.range2 (Nat.pow 10 5) (Nat.pow 10 6)
|>.filter (
fun n =>
let digits_sum := Nat.toDigits 10 n
|>.map (Char.toNat . - '0'.toNat)
|>.foldl Nat.add 0
digits_sum = 10)
|>.length
IO.println answer
Kyle Miller (Apr 28 2024 at 00:52):
Instead of Nat.pow
, it's customary to use ^
.
For style, I probably would format the second one like
def main : IO Unit :=
let answer :=
List.range2 (10 ^ 5) (10 ^ 6)
|>.filter (fun n =>
let digits := Nat.toDigits 10 n |>.map (Char.toNat · - '0'.toNat)
digits.foldl (· + ·) 0 = 10)
|>.length
IO.println answer
I feel like it's better to name intermediate results rather than using long pipelines. I could have also named digits_sum
here, but digits.foldl (· + ·) 0
is a clear enough fold.
That said, it's better to make small helper functions. Mathlib has functions such as docs#Nat.digits and docs#List.sum already, and you could define these too.
Kyle Miller (Apr 28 2024 at 00:56):
Here's how you can avoid turning digits into characters and back:
def main : IO Unit :=
let answer := List.range2 (10 ^ 5) (10 ^ 6) |>.map digitSum |>.filter (· = 10) |>.length
IO.println answer
where
digitSum (n : Nat) : Nat :=
if n = 0 then
0
else
(n % 10) + digitSum (n / 10)
Kyle Miller (Apr 28 2024 at 00:57):
Or rather than filtering and taking the length, there's docs#List.count from std:
def main : IO Unit :=
let answer := List.range2 (10 ^ 5) (10 ^ 6) |>.map digitSum |>.count 10
IO.println answer
where
digitSum (n : Nat) : Nat :=
if n = 0 then
0
else
(n % 10) + digitSum (n / 10)
Kyle Miller (Apr 28 2024 at 01:02):
Though you could make digitSum
be tail recursive by having it accumulate:
digitSum (n : Nat) (acc : Nat := 0) : Nat :=
if n = 0 then
acc
else
digitSum (n / 10) (acc + n % 10)
Last updated: May 02 2025 at 03:31 UTC