F#中数字到罗马数字函数的进一步优化



我是F#的新手,我很好奇这是否还能进一步优化。我不太确定我是否也做对了。我很好奇,尤其是最后一行,因为它看起来又长又丑。

我在谷歌上搜索过,但只有罗马数字对数字的解决方案出现,所以我很难进行比较。

type RomanDigit = I | IV | V | IX
let rec romanNumeral number =
    let values = [ 9; 5; 4; 1 ]
    let capture number values =
            values
            |> Seq.find ( fun x -> number >= x )
    let toRomanDigit x =
        match x with
        | 9 -> IX
        | 5 -> V
        | 4 -> IV
        | 1 -> I
    match number with
    | 0 -> []
    | int -> Seq.toList ( Seq.concat [ [ toRomanDigit ( capture number values ) ]; romanNumeral ( number - ( capture number values ) ) ] )

感谢任何能帮助解决这个问题的人。

递归查找可以从值中减去的最大数字表示的一种稍短的方法(使用List.fund):

let units =
    [1000, "M"
     900, "CM"
     500, "D"
     400, "CD"
     100, "C"
     90, "XC"
     50, "L"
     40, "XL"
     10, "X"
     9, "IX"
     5, "V"
     4, "IV"
     1, "I"]
let rec toRomanNumeral = function
    | 0 -> ""
    | n ->
        let x, s = units |> List.find (fun (x,s) -> x <= n)
        s + toRomanNumeral (n-x)

如果我必须使用区分并集来表示罗马字母,我将不包括IV和IX。

type RomanDigit = I|V|X
let numberToRoman n =       
   let (r, diff) =
        if   n > 8 then [X], n - 10
        elif n > 3 then [V], n -  5
        else [], n
   if diff < 0 then I::r
   else r @ (List.replicate diff I)

然后,在这个解决方案的基础上,您可以更进一步,将其扩展到所有数字。

这是我的第一次尝试,使用折叠和部分应用程序:

type RomanDigit = I|V|X|L|C|D|M
let numberToRoman n i v x = 
    let (r, diff) =
        if   n > 8 then [x], n - 10
        elif n > 3 then [v], n -  5
        else [], n
    if diff < 0 then i::r
    else r @ (List.replicate diff i)
let allDigits (n:int) =
    let (_, f) = 
        [(I,V); (X,L); (C,D)] 
        |> List.fold (fun (n, f) (i, v) -> 
            (n / 10, fun x -> (numberToRoman (n % 10) i v x) @ f i)) (n, (fun _ -> []))
    f M

以下是@Philip Trelford答案的尾部递归版本:

let toRomanNumeral n =
    let rec iter acc n =
        match n with
        | 0 -> acc
        | n ->
            let x, s = units |> List.find (fun (x, _) -> x <= n)
            iter (acc + s) (n-x)
    iter "" n

最新更新