函数参数为null,即使传递了非null参数

时间:2021-10-20 02:33:01

F# newbie here, and sorry for the bad title, I'm not sure how else to describe it.
Very strange problem I'm having. Here's the relevant code snippet:

F#newbie在这里,对不好的标题感到抱歉,我不知道怎么形容它。我有很奇怪的问题。这是相关的代码段:

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

I step through it with Visual Studio and it clearly shows that x in getRelativeTime is a 3-tuple with a well-formed datetime string. But when I step to calcRelTime item is null. Everything ends up returning a 3-tuple that has the original datetime string, instead of one with the total minutes past. There's no other errors anywhere, until the that datetime string hits a function that expects it to be an integer string.

我使用Visual Studio逐步完成它,它清楚地显示getRelativeTime中的x是一个具有格式良好的日期时间字符串的3元组。但是当我执行calcRelTime项时为null。一切都会返回一个具有原始日期时间字符串的3元组,而不是一个总分钟过去的元组。在任何地方都没有其他错误,直到该datetime字符串命中一个期望它是整数字符串的函数。

Any help would be appreciated! (along with any other F# style tips/suggestions for these functions).

任何帮助,将不胜感激! (以及这些功能的任何其他F#样式提示/建议)。

2 个解决方案

#1


item is null, because it hasn't been constructed yet out of its parts. The F# compiler compiles tupled parameters as separate actual (IL-level) parameters rather than one parameter of type Tuple<...>. If you look at your compiled code in ILSpy, you will see this signature (using C# syntax):

item为null,因为尚未构建其中的部分。 F#编译器将tupled参数编译为单独的实际(IL级)参数,而不是Tuple <...>类型的一个参数。如果您在ILSpy中查看已编译的代码,您将看到此签名(使用C#语法):

public static Tuple<string, string, string> calcRelTime(string item_0, string item_1, string item_2)

This is done for several reasons, including interoperability with other CLR languages as well as efficiency.

这样做有几个原因,包括与其他CLR语言的互操作性以及效率。

To be sure, the tuple itself is then constructed from these arguments (unless you have optimization turned on), but not right away. If you make one step (hit F11), item will obtain a proper non-null value.

可以肯定的是,元组本身是由这些参数构造的(除非你打开了优化),但不是马上。如果你做了一步(点击F11),item将获得一个正确的非null值。

You can also see these compiler-generated parameters if you go to Debug -> Windows -> Locals in Visual Studio.

如果在Visual Studio中转到Debug - > Windows - > Locals,也可以看到这些编译器生成的参数。

As for why it's returning the original list instead of modified one, I can't really say: on my setup, everything works as expected:

至于为什么它返回原始列表而不是修改后的列表,我不能说:在我的设置上,一切都按预期工作:

> getRelativeTime [] [("x","05/01/2015","y")]
val it : (string * string * string) list = [("x", "y", "17305")]

Perhaps if you share your test code, I would be able to tell more.

也许如果你分享你的测试代码,我就能说出更多。

And finally, what you're doing can be done a lot simpler: you don't need to write a recursive loop yourself, it's already done for you in the many functions in the List module, and you don't need to accept a tuple and then deconstruct it using tFst, tSnd, and tTrd, the compiler can do it for you:

最后,你正在做的事情可以做得更简单:你不需要自己编写一个递归循环,它已经在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

#2


let getRelativeTime' list = 
    let calc (a, b, c) = (a, c, (floor (DateTime.Now - (DateTime.Parse b)).TotalMinutes).ToString())
    list |> List.map calc

Signature of the function is val getRelativeTime : list:('a * string * 'b) list -> ('a * 'b * string) list

该函数的签名是val getRelativeTime:list :('a * string *'b)list - >('a *'b * string)list

You can deconstruct item in the function declaration to (a, b, c), then you don't have to use the functions tFst, tSnd and tTrd.

您可以将函数声明中的项解构为(a,b,c),然后您不必使用函数tFst,tSnd和tTrd。

The List module has a function map that applies a function to each element in a list and returns a new list with the mapped values.

List模块有一个函数映射,它将函数应用于列表中的每个元素,并返回带有映射值的新列表。

#1


item is null, because it hasn't been constructed yet out of its parts. The F# compiler compiles tupled parameters as separate actual (IL-level) parameters rather than one parameter of type Tuple<...>. If you look at your compiled code in ILSpy, you will see this signature (using C# syntax):

item为null,因为尚未构建其中的部分。 F#编译器将tupled参数编译为单独的实际(IL级)参数,而不是Tuple <...>类型的一个参数。如果您在ILSpy中查看已编译的代码,您将看到此签名(使用C#语法):

public static Tuple<string, string, string> calcRelTime(string item_0, string item_1, string item_2)

This is done for several reasons, including interoperability with other CLR languages as well as efficiency.

这样做有几个原因,包括与其他CLR语言的互操作性以及效率。

To be sure, the tuple itself is then constructed from these arguments (unless you have optimization turned on), but not right away. If you make one step (hit F11), item will obtain a proper non-null value.

可以肯定的是,元组本身是由这些参数构造的(除非你打开了优化),但不是马上。如果你做了一步(点击F11),item将获得一个正确的非null值。

You can also see these compiler-generated parameters if you go to Debug -> Windows -> Locals in Visual Studio.

如果在Visual Studio中转到Debug - > Windows - > Locals,也可以看到这些编译器生成的参数。

As for why it's returning the original list instead of modified one, I can't really say: on my setup, everything works as expected:

至于为什么它返回原始列表而不是修改后的列表,我不能说:在我的设置上,一切都按预期工作:

> getRelativeTime [] [("x","05/01/2015","y")]
val it : (string * string * string) list = [("x", "y", "17305")]

Perhaps if you share your test code, I would be able to tell more.

也许如果你分享你的测试代码,我就能说出更多。

And finally, what you're doing can be done a lot simpler: you don't need to write a recursive loop yourself, it's already done for you in the many functions in the List module, and you don't need to accept a tuple and then deconstruct it using tFst, tSnd, and tTrd, the compiler can do it for you:

最后,你正在做的事情可以做得更简单:你不需要自己编写一个递归循环,它已经在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

#2


let getRelativeTime' list = 
    let calc (a, b, c) = (a, c, (floor (DateTime.Now - (DateTime.Parse b)).TotalMinutes).ToString())
    list |> List.map calc

Signature of the function is val getRelativeTime : list:('a * string * 'b) list -> ('a * 'b * string) list

该函数的签名是val getRelativeTime:list :('a * string *'b)list - >('a *'b * string)list

You can deconstruct item in the function declaration to (a, b, c), then you don't have to use the functions tFst, tSnd and tTrd.

您可以将函数声明中的项解构为(a,b,c),然后您不必使用函数tFst,tSnd和tTrd。

The List module has a function map that applies a function to each element in a list and returns a new list with the mapped values.

List模块有一个函数映射,它将函数应用于列表中的每个元素,并返回带有映射值的新列表。