This question already has answers here:
Efficient conversion from String[] to F# List
(3 answers)
Closed 9 years ago.
Is there an easy way to convert a string array into a list of strings in F#? I'm rather new to F# and I can't find anything on this while searching.
You can use Array.toList:
let myArray = [| "foo"; "bar"; "baz" |]
let myList = myArray |> Array.toList
Or for that matter, Seq.toList.
A universal way is with library function from source collection type module Array.toList : 'T [] -> 'T list. It works for any type of elements 'T.
In particular, for 'T being string:
[| "S1"; "S2"; "S3" |] |> Array.toList
Or similarly universally, but with library function from target collection type moduleList.ofArray : 'T [] -> 'T list:
[| "S1"; "S2"; "S3" |] |> List.ofArray
Related
This question already has an answer here:
How to use overloaded explicit conversion operators?
(1 answer)
Closed 4 years ago.
Let's say that I have a class in C# with overloaded implicit and explicit operators:
public static implicit operator CSClass(int a) => ...;
public static explicit operator int(CSClass a) => ...;
I compile this project as class library.
In F# now I can add my operator for implicit conversions and use it:
#r #"C:\path\to.dll"
open Some.Namespace.ToMyClass
let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)
let a : CSClass = !> 5
But how can I do an explicit overloaded conversion in F#? (CSClass to int)
It is my understanding that F# does not usually do explicit conversions. Instead, you would just use a function. For example, if you have a char and want to convert that explicitly into an int, in C# you write:
char theChar = 'A';
int convertedChar = (int)theChar;
In F#, the int operator (function) is used for the same purpose:
let theChar = 'A'
let convertedChar = int theChar;
Therefore the idiomatic way to do the conversion would be something like this:
module Some.Namespace.MyClass
let toInt (x : MyClass) = [...]
You would use it like so:
let convertedMyClass = MyClass.toInt myClass
It can also be piped:
funcReturningMyClass x y
|> MyClass.toInt
|> printfn "%d"
I would like to extend F# Arrays such that I can use arrays without converting to the finite int. Instead I want to work with bigint directly.
I was able to add a second length method to the array type as follows:
type 'T ``[]`` with
member this.LengthI: bigint =
bigint this.Length
member this.Item(index: bigint): 'T =
this.[int index]
However the Item method cannot be called with the .[ ] syntax.
Any ideas how this could be achieved? I this possible at all?
I strongly suspect this isn't possible for native arrays. You can verify yourself that you can overload indexed access just fine for other collections.
If you compile the following code:
let myArray = [| "a" |]
let myList = [ "a" ]
let arrayElement = myArray.[11111]
let listElement = myList.[22222]
and inspect the resulting IL, you'll see that while accessing the list element compiles to a regular virtual call, there is a special CIL instruction for accessing a native array element, ldelem.
//000004: let arrayElement = myArray.[11111]
IL_002c: call string[] Fuduoqv1565::get_myArray()
IL_0031: ldc.i4 0x2b67
IL_0036: ldelem [mscorlib]System.String
IL_003b: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::arrayElement#4
.line 5,5 : 1,33 ''
//000005: let listElement = myList.[22222]
IL_0040: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string> Fuduoqv1565::get_myList()
IL_0045: ldc.i4 0x56ce
IL_004a: callvirt instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>::get_Item(int32)
IL_004f: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::listElement#5
IL_0054: ret
I would guess that the same compiler logic that special-case array access to that single instruction also bypass any overload resolution involving extension methods and the like.
One way to circumvent this is to wrap the array in a custom type, where overloaded indexers will work as you expect. Making the wrapper type a struct should reduce the performance loss in most cases:
type [<Struct>] BigArray<'T>(array : 'T[]) =
member this.LengthI: bigint =
bigint array.Length
member this.Item
with get(index : int) = array.[index]
and set (index : int) value = array.[index] <- value
member this.Item
with get(index : bigint) = array.[int index]
and set (index : bigint) value = array.[int index] <- value
let bigArray = BigArray myArray
let bigArrayElement = bigArray.[0]
let bigArrayElement2 = bigArray.[bigint 0]
Another one is to upcast the array to the base System.Array class, on which you can then define the same overloaded operator. This removes the need to create a wrapper type and duplicate all members of 'T[], as you can just upcast/downcast the same array object as necessary. However, since the base class is untyped, you will lose type safety and have to box/unbox the elements when using the indexed access, which is quite ugly:
type System.Array with
member this.Item
with get (index : int) = (this :?> 'T[]).[index]
and set (index : int) (value : 'T) = (this :?> 'T[]).[index] <- value
member this.Item
with get(index : bigint) : 'T = (this :?> 'T[]).[int index]
and set(index : bigint) (value : 'T) = (this :?> 'T[]).[int index] <- value
let untypedArray = myArray :> System.Array
let untypedArrayElement = box untypedArray.[0] :?> string
let untypedArrayElement2 = box untypedArray.[bigint 0] :?> string
Please see the below code.
let x = Seq.head [1.0; 2.0] // This is ok.
type Func<'T> = { f: seq<'T> -> 'T }
let func = { f = Seq.head }
// Compilation error: This expression was expected to have type seq<obj> but here has type 'a list
let y = func.f [1.0; 2.0]
let z = func.f ([1.0; 2.0] |> List.toSeq) // This is ok.
I don't understand why Seq.head and fund.f behavior differently here. It looks like a compiler bug to me. However, if this is by design, can anyone help explain a little bit to me? Thanks a lot!
The below is the answer from Don Syme (github.com/fsharp):
This is by design. The rule called "14.4.3 Implicit Insertion of Flexibility for Uses of Functions and Members" is only applied to uses of functions and members, not uses of record fields.
let dic = Environment.GetEnvironmentVariables()
dic
|> Seq.filter( fun k -> k.Contains("COMNTOOLS"))
fails to compile.
I've tried using Array.filter, Seq.filter, List.filter
I've tried getting just the dic.Keys to iterate over but F# doesn't seem to want me to coerce a KeyCollection into an IEnumerable.
I've tried upcasting the hashtable into an IEnumerable<KeyValuePair<string,string>>
How do I walk the hashtable that is returned from Environment.GetEnvironmentVariables() ?
Since Environment.GetEnvironmentVariables() returns a non-generic IDictionary and it stores key/value pairs in DictionaryEntry, you have to use Seq.cast first:
let dic = Environment.GetEnvironmentVariables()
dic
|> Seq.cast<DictionaryEntry>
|> Seq.filter(fun entry -> entry.Key.ToString().Contains("COMNTOOLS"))
See the relevant docs at https://msdn.microsoft.com/en-us/library/system.collections.idictionary(v=vs.110).aspx. Notice that entry.Key is of type obj, so one has to convert to string before checking string containment.
Instead of using high-order functions, sequence expression might be handy:
let dic = Environment.GetEnvironmentVariables()
seq {
for entry in Seq.cast<DictionaryEntry> dic ->
(string entry.Key), (string entry.Value)
}
|> Seq.filter(fun (k, _) -> k.Contains("COMNTOOLS"))
F# Seq can only operate with System.Collections.Generic.IEnumerable<_>. System.IDictionary returned by Environment.GetEnvironmentVariables is not generic but it implements non-generic System.Collections.IEnumerable and not System.Collections.Generic.IEnumerable<_>. System.Collections.IEnumerable does not contain type information and allows enumeration of the collection of boxed types i.e. instances of System.Object.
Anyway System.IDictionary can be enumerated as collection of System.Collections.DictionaryEntry objects so you can simply call Seq.cast on it. It will give you access to Key and Value properties, yet still boxed as objects, so you should unbox them too.
let dic = System.Environment.GetEnvironmentVariables()
dic
|> Seq.cast<System.Collections.DictionaryEntry>
|> Seq.filter( fun k -> (k.Key :?> string).Contains("COMNTOOLS"))
Alternatively you can use the following function
let asStringPairSeq (d : System.Collections.IDictionary) : seq<string * string> =
Seq.cast<System.Collections.DictionaryEntry> d
|> Seq.map (fun kv -> kv.Key :?> string, kv.Value :?> string)
System.Environment.GetEnvironmentVariables()
|> asStringPairSeq
|> Seq.filter (fun (k,v) -> k.Contains("COMNTOOLS"))
I have an array and I want to cast it to an object. This is my code.
let a = [| 1 |]
let b = a :?> obj
but it tells me that int [] has no proper subtypes and cannot be used as a source of runtime type coercion. I'm pretty sure I can always do (object) in C# without the compiler complaining, so what's the issue here?
You're trying to downcast (:?>), instead of upcast (:>). Your code should be:
let a = [| 1 |]
let b = a :> obj
Please see http://msdn.microsoft.com/en-us/library/dd233220.aspx for more details.
While ebb's answer is of course correct, there is another solution for the special case of upcasting to obj, which is the box operator:
let a = [| 1 |]
let b = box a
In F#, box doesn't have the same meaning as it does in the CLR, where it means "make a reference-type object corresponding to a value-type value." In F#, it just means "cast to obj." Because of this, you can use box with reference types as well as with value types.