我正在表演一个卡塔而且失败了:
有人可以提供有关我如何构建以下代码的指导:
将整数转换为罗马数字
type RomanNumerals = | I // 1
| IV // 4
| V // 5
| X // 10
| L // 50
| C // 100
| D // 500
| M // 1000
let getOccurrances integer unit symbol =
[for i in [1..(integer / unit)] -> symbol]
let convertInteger = function
| v when v > 1000 -> getOccurrances v 1000 M
| v when v > 500 -> getOccurrances v 500 D
| v when v > 100 -> getOccurrances v 100 C
| v when v > 50 -> getOccurrances v 50 L
| v when v > 10 -> getOccurrances v 10 X
| v when v > 5 -> getOccurrances v 5 V
| v when v > 4 -> getOccurrances v 4 IV
| v when v > 1 -> getOccurrances v 1 I
| _ -> []
let romanNumerals = convertInteger 3001
printf "%A" romanNumerals
答案 0 :(得分:3)
主要问题是你没有使用递归,所以它与3001匹配,3001大于1000,因此它只需要放3 M
。但是,您希望以递归方式执行此操作,直到它降至0。
这是一个有效的例子.....确实不是很好......
type RomanNumerals = | I // 1
| IV // 4
| V // 5
| X // 10
| L // 50
| C // 100
| D // 500
| M // 1000
let getOccurrances integer unit symbol =
[for i in [1..(integer / unit)] -> symbol]
let rec convertInteger (romanList: RomanNumerals list) v =
match v with
| v when v >= 1000 -> let temp = getOccurrances v 1000 M
convertInteger (temp|> List.append(romanList)) (v - temp.Length * 1000)
| v when v >= 500 -> let temp = getOccurrances v 500 D
convertInteger (temp |> List.append(romanList)) (v - temp.Length * 500)
| v when v >= 100 -> let temp = getOccurrances v 100 C
convertInteger (temp |> List.append(romanList)) (v - temp.Length * 100)
| v when v >= 50 -> let temp = getOccurrances v 50 L
convertInteger (temp |> List.append(romanList)) (v - temp.Length * 50)
| v when v >= 10 -> let temp = getOccurrances v 10 X
convertInteger (temp |> List.append(romanList)) (v - temp.Length * 10)
| v when v >= 5 -> let temp = getOccurrances v 5 V
convertInteger (temp |> List.append(romanList)) (v - temp.Length * 5)
| v when v >= 4 -> let temp = getOccurrances v 4 IV
convertInteger (temp |> List.append(romanList)) (v - temp.Length * 4)
| v when v >= 1 -> let temp = getOccurrances v 1 I
convertInteger (temp|> List.append(romanList)) (v - temp.Length * 1)
| _ -> romanList
[<EntryPoint>]
let main argv =
let romanNumerals = convertInteger [] 3001
printf "%A" romanNumerals
//returns [M; M; M; I]
0 // return an integer exit code
答案 1 :(得分:3)
@Ringil在评论中有一个很好的领先:使用与union案例匹配的列表及其值导致我认为更惯用的代码:
type RomanNumerals =
| I // 1
| IV // 4
| V // 5
| IX // 9
| X // 10
| XL // 40
| L // 50
| XC // 90
| C // 100
| CD // 400
| D // 500
| CM // 900
| M // 1000
with
// This associates the DU cases to their values
static member values = [
M, 1000
CM, 900
D, 500
CD, 400
C, 100
XC, 90
L, 50
XL, 40
X, 10
IX, 9
V, 5
IV, 4
I, 1
]
let rec convertInteger i =
// Base case: We've reached zero, so return an empty list
if i = 0 then
[]
// Otherwise...
else
// Try to find the first symbol for which the value is <= of the remaining input
match List.tryFind (fun (symbol, value) -> value <= i) RomanNumerals.values with
| Some (symbol, value) ->
// If there is one, prepend it to the result of recursion
symbol :: convertInteger (i - value)
| None ->
// If there is non, we've messed up somehow so throw an exception.
// Since we have a case for I = 1, this should never happen but we
// still have to handle it explicitely - otherwise, the compiler complains
// about a missing case in the match.
failwithf "Got a non-zero integer that isn't matched by an existing numeral: %i" i
[<EntryPoint>]
let main argv =
let input = 3001
let romanNumerals = convertInteger input
// Print the resulting list
printfn "%A" romanNumerals // prints [M; M; M; I]
// Alternatively, print each character individually:
List.iter (printf "%A") romanNumerals // prints MMMI
printfn ""
0
注意,如上所述,每次对convertInteger的递归调用都必须:
因为它必须在递归调用之后才能工作,所以它将无法利用tail call optimization。解决此问题的方法是将其更改为以下代码:
let convertInteger i =
// Define an inner recursive function that carries the intermediate state as an argument
let rec loop result i =
// Base case: We've reached zero, so return the current result
if i = 0 then
result
// Otherwise...
else
// Try to find the first symbol for which the value is <= of the remaining input
match List.tryFind (fun (symbol, value) -> value <= i) RomanNumerals.values with
| Some (symbol, value) ->
// If there is one, prepend the state to the result
// Since we're always prepending, we'll have to reverse
// the list once we're done but that's okay
let newResult = symbol :: result // `a :: b` means "prepend element a to list b"
// Note that this is a tail call, since nothing is done with
// the result once the function returns. This allows for tail call optimization
loop newResult (i - value)
| None ->
// If there is non, we've messed up somehow so throw an exception.
// Since we have a case for I = 1, this should never happen but we
// still have to handle it explicitely - otherwise, the compiler complains
// about a missing case in the match.
failwithf "Got a non-zero integer that isn't matched by an existing numeral: %i" i
// Then call the function with an initial state - an empty list in this case
let result = loop [] i
// And reverse the result, since it was built backwards
List.rev result
这就是我通常在F#中编写递归函数的方法:一个带有简单接口的非递归外部函数 - 在这种情况下只是要转换的整数 - 以及一个传递状态的内部递归函数。
随意询问有关代码的任何问题 - 我已经尝试在我的评论中做到彻底,但我可能错过了部分内容。 =)