Function argument is null, even though a non-null argument is passed

末鹿安然 提交于 2019-12-13 04:35:24

问题


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:

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.

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


回答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):

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.

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.

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

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:

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

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.

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.



来源:https://stackoverflow.com/questions/30205130/function-argument-is-null-even-though-a-non-null-argument-is-passed

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!