函数参数为空,即使传递了 non-null 参数
Function argument is null, even though a non-null argument is passed
这里是 F# 新手,很抱歉标题不好,我不知道该怎么描述它。
我遇到了非常奇怪的问题。这是相关的代码片段:
let calcRelTime (item :(string * string * string)) =
tSnd item
|>DateTime.Parse
|> fun x -> DateTime.Now - x
|> fun y -> (floor y.TotalMinutes).ToString()
|>makeTriple (tFst item) (tTrd item) //makeTriple switches y & z. How do I avoid having to do that?
let rec getRelativeTime f (l :(string * string * string) list) =
match l with
| [] -> f
| x :: xs -> getRelativeTime (List.append [calcRelTime x] f) xs
我用 Visual Studio 单步执行它,它清楚地表明 getRelativeTime 中的 x 是一个带有 well-formed 日期时间字符串的三元组。但是当我进入 calcRelTime item 时,它是空的。一切最终都会返回一个具有原始日期时间字符串的三元组,而不是一个包含过去总分钟数的元组。在该日期时间字符串遇到期望它是整数字符串的函数之前,任何地方都没有其他错误。
如有任何帮助,我们将不胜感激! (以及这些函数的任何其他 F# 样式 tips/suggestions)。
item
为空,因为它还没有从它的部分构造出来。 F# 编译器将元组参数编译为单独的实际(IL 级)参数,而不是 Tuple<...>
类型的一个参数。如果您在 ILSpy 中查看编译后的代码,您将看到此签名(使用 C# 语法):
public static Tuple<string, string, string> calcRelTime(string item_0, string item_1, string item_2)
这样做有几个原因,包括与其他 CLR 语言的互操作性以及效率。
可以肯定的是,元组本身是根据这些参数构造的(除非你打开了优化),但不是马上。如果您迈出一步(按 F11),item
将获得一个正确的非空值。
如果在 Visual Studio 中转到调试 -> Windows -> 局部变量,也可以看到这些编译器生成的参数。
至于为什么它返回原始列表而不是修改后的列表,我真的不能说:在我的设置中,一切都按预期工作:
> getRelativeTime [] [("x","05/01/2015","y")]
val it : (string * string * string) list = [("x", "y", "17305")]
也许如果你分享你的测试代码,我可以告诉更多。
最后,你正在做的事情可以做得更简单:你不需要自己写一个递归循环,它已经在 List
模块的许多函数中为你完成了,并且您不需要接受一个元组然后使用 tFst
、tSnd
和 tTrd
解构它,编译器可以为您完成:
let getRelativeTime lst =
let calcRelTime (x, time, y) =
let parsed = DateTime.Parse time
let since = DateTime.Now - parsed
let asStr = (floor since.TotalMinutes).ToString()
(x, asStr, y)
List.map calRelTime lst
let getRelativeTime' list =
let calc (a, b, c) = (a, c, (floor (DateTime.Now - (DateTime.Parse b)).TotalMinutes).ToString())
list |> List.map calc
函数的签名是val getRelativeTime : list:('a * string * 'b) list -> ('a * 'b * string) list
可以将函数声明中的item
解构为(a, b, c)
,这样就不用再使用函数tFst
、tSnd
和[=16=了].
List 模块有一个函数 map
将一个函数应用于列表中的每个元素,returns 一个具有映射值的新列表。
这里是 F# 新手,很抱歉标题不好,我不知道该怎么描述它。
我遇到了非常奇怪的问题。这是相关的代码片段:
let calcRelTime (item :(string * string * string)) =
tSnd item
|>DateTime.Parse
|> fun x -> DateTime.Now - x
|> fun y -> (floor y.TotalMinutes).ToString()
|>makeTriple (tFst item) (tTrd item) //makeTriple switches y & z. How do I avoid having to do that?
let rec getRelativeTime f (l :(string * string * string) list) =
match l with
| [] -> f
| x :: xs -> getRelativeTime (List.append [calcRelTime x] f) xs
我用 Visual Studio 单步执行它,它清楚地表明 getRelativeTime 中的 x 是一个带有 well-formed 日期时间字符串的三元组。但是当我进入 calcRelTime item 时,它是空的。一切最终都会返回一个具有原始日期时间字符串的三元组,而不是一个包含过去总分钟数的元组。在该日期时间字符串遇到期望它是整数字符串的函数之前,任何地方都没有其他错误。
如有任何帮助,我们将不胜感激! (以及这些函数的任何其他 F# 样式 tips/suggestions)。
item
为空,因为它还没有从它的部分构造出来。 F# 编译器将元组参数编译为单独的实际(IL 级)参数,而不是 Tuple<...>
类型的一个参数。如果您在 ILSpy 中查看编译后的代码,您将看到此签名(使用 C# 语法):
public static Tuple<string, string, string> calcRelTime(string item_0, string item_1, string item_2)
这样做有几个原因,包括与其他 CLR 语言的互操作性以及效率。
可以肯定的是,元组本身是根据这些参数构造的(除非你打开了优化),但不是马上。如果您迈出一步(按 F11),item
将获得一个正确的非空值。
如果在 Visual Studio 中转到调试 -> Windows -> 局部变量,也可以看到这些编译器生成的参数。
至于为什么它返回原始列表而不是修改后的列表,我真的不能说:在我的设置中,一切都按预期工作:
> getRelativeTime [] [("x","05/01/2015","y")]
val it : (string * string * string) list = [("x", "y", "17305")]
也许如果你分享你的测试代码,我可以告诉更多。
最后,你正在做的事情可以做得更简单:你不需要自己写一个递归循环,它已经在 List
模块的许多函数中为你完成了,并且您不需要接受一个元组然后使用 tFst
、tSnd
和 tTrd
解构它,编译器可以为您完成:
let getRelativeTime lst =
let calcRelTime (x, time, y) =
let parsed = DateTime.Parse time
let since = DateTime.Now - parsed
let asStr = (floor since.TotalMinutes).ToString()
(x, asStr, y)
List.map calRelTime lst
let getRelativeTime' list =
let calc (a, b, c) = (a, c, (floor (DateTime.Now - (DateTime.Parse b)).TotalMinutes).ToString())
list |> List.map calc
函数的签名是val getRelativeTime : list:('a * string * 'b) list -> ('a * 'b * string) list
可以将函数声明中的item
解构为(a, b, c)
,这样就不用再使用函数tFst
、tSnd
和[=16=了].
List 模块有一个函数 map
将一个函数应用于列表中的每个元素,returns 一个具有映射值的新列表。