If I am using reference tuples, this compiles:
let plot(x: int, y: int) = ()
let point = 3, 4
plot(fst point, snd point)
However, if I am using struct tuples...
let plot(x: int, y: int) = ()
let point = struct (3, 4)
plot(fst point, snd point)
... I get the compiler error, One tuple type is a struct tuple, the other is a reference tuple
What should I do?
There's a ToTuple() extension method in System.TupleExtensions for ValueTuple<T1, T2...>.
You could just call:
plot (point.ToTuple())
As for fst, snd, they're bound to System.Tuple<>, so maybe you could define an alternative:
let fstv struct (a,_) = a
let sndv struct (_,b) = b
In F# 4.7, you must add another line to decompose the tuple.
let plot(x: int, y: int) = ()
let point = struct (3, 4)
let struct (x, y) = point
plot(x, y)
You can declare new functions that work on struct tuples:
let fstv (struct (a, _)) = a
let sndv (struct (_, b)) = b
Usage:
let plot(x: int, y: int) = ()
let point = struct (3, 4)
plot(fstv point, sndv point)
If you want to get clever, you can use SRTP to make new fst and snd functions that work with both struct tuples and regular tuples:
type PairDispatcher =
| PairDispatcher with
static member inline ($) (PairDispatcher, (a, b)) = fun f -> f a b
static member inline ($) (PairDispatcher, struct (a, b)) = fun f -> f a b
let inline fst x = (PairDispatcher $ x) (fun a _ -> a)
let inline snd x = (PairDispatcher $ x) (fun _ b -> b)
(Taken from http://www.fssnip.net/7TT/title/Generic-fst-and-snd-functions)
Then usage is:
let plot(x: int, y: int) = ()
let point = struct (3, 4)
plot(fst point, snd point)
I think on balance I would prefer to declare new functions.
Related
This cannot work:
let a () =
async {
return 0
}
let b x =
async {
return
match x with
| true ->
let! y = a() <- you can't compile this
y
| false ->
0
}
I understand I could do this:
let b x =
async {
match x with
| true ->
return! a()
| false ->
return 0
}
but there are cases where I need a:
let! y = a()
to do more operations with the result.
Is there an elegant way to achieve this?
Can't you combine the two?
let b x =
async {
match x with
| true ->
let! y = a()
return y
| false ->
return 0
}
You can move the async expression inside each case:
let b x =
match x with
| true -> async {
let! y = a ()
...
return y
}
| false -> async { return 0 }
I don't know if you think this is more elegant, but more functions usually makes it look a bit nicer, here's an example:
module Async =
let create v = async { return v }
let bind f a = async.Bind (a, f)
let map f a =
bind (f >> create) a
let a () = Async.create 0
let addOne y =
y + 1
let b x =
match x with
| true -> a () |> Async.map addOne
| false -> Async.create 0
// Additional piping to map or bind if you wish, e.g:
// |> Async.bind ...
You can do the return inside the match.
let a () = async {
return 0
}
let b x = async {
match x with
| true -> return! a()
| false -> return 0
}
Otherwise you can do this:
module Async =
let create x = async { return x }
let a () = async {
return 10
}
let b x = async {
let! x1 =
match x with
| true -> a()
| false -> async { return 0 }
let! x2 =
if x then a() else Async.create 0
// Do whatever with x1 or x2
return x2
}
Create a function for code that occur often:
let whatever b a x =
if x then a() else Async.create x
let! y = whatever b a 0
I have created one function to add tuples which looks like (Int, Int).
func +<T : Numeric> (x: (T, T), y: (T, T)) -> (T, T) {
return (x.0 + y.0, x.1 + y.1)
}
It works for (10, 20) + (15, 15)
Now what I need is to make an advancement to function to accept any variable length tuples of same length.
How is possible way?
At the end (12) + (23) and (10, 12, 16) + (11, 36, 25) should work.
Tuples need to have their number of elements determined at compile time, thus a variadic-like function won't work. You'll need to add overrides for the + operator, for each tuple size you need to support:
func +<T : Numeric> (x: (T, T), y: (T, T)) -> (T, T) {
return (x.0 + y.0, x.1 + y.1)
}
func +<T : Numeric> (x: (T, T, T), y: (T, T, T)) -> (T, T, T) {
return (x.0 + y.0, x.1 + y.1, x.2 + y.2)
}
func +<T : Numeric> (x: (T, T, T, T), y: (T, T, T, T)) -> (T, T, T, T) {
return (x.0 + y.0, x.1 + y.1, x.2 + y.2, x.3 + y.3)
}
// and so on, ...
Alternatively, you can switch to other data types, like arrays, which allow a dynamic number of items:
infix operator ++
func ++<T: Numeric>(_ lhs: [T], _ rhs: [T]) -> [T] {
return zip(lhs, rhs).map { $0.0 + $0.1 }
}
print([10, 12, 16] ++ [11, 36, 25]) // [21, 48, 41]
Caveats of this approach:
you need to use a different operator, since is + already defined for arrays, and it concatenates the arrays instead of individually summing the corresponding elements
if the two arrays have different sizes, then the result will have the size of the smaller arrays of the two input ones.
You can use the Array solution as suggested by #Cristik or you can also make use of closure returning variadic function like:
func add<T : Numeric>(_ a: T...) -> (_ b: T...) -> [T] {
return { (b: T...) -> [T] in
return zip(a, b).map { $0.0 + $0.1 }
}
}
let sum = add(1, 2,3)(4, 5, 6)
print(sum)
I want to define the expected data type of the elements of a tuple which will be passed to call a function. When I don't define it and let the type inference work it is ok, but in the moment I want to write small functions that are still not called anywhere I don't get how to define the arguments.
This example. I expect description to be a tuple where every of the five elements is a int and then extract each of the parts to work with then.
let setArray (description: int * int * int * int * int) =
let id = fun (i, _, _, _, _) -> i
let startX = fun (_, x, _, _, _) -> x
let startY = fun (_, _, y, _, _) -> y
let width = fun (_, _, _, w, _) -> w
let height = fun (_, _, _, _, h) -> h
let arrayResult = Array2D.init (width + 1) (height + 1) (fun i j -> if i < width && j < height then id)
arrayResult
.fsx(29,45): error FS0001: The type 'int' does not match the type ''a * 'b * 'c * 'd * 'e -> 'd'
For other functions, like I said, type inference works and I can use the pattern matching without problem
let getMaxCoords =
let x = elements |> Seq.map (fun (_, x, _, _, _) -> x) |> Seq.max
let y = elements |> Seq.map (fun (_, _, y, _, _) -> x) |> Seq.max
x, y
What am I doing wrong?
First, the following code block is defining five functions, not five integer values:
let setArray (description: int * int * int * int * int) =
let id = fun (i, _, _, _, _) -> i
let startX = fun (_, x, _, _, _) -> x
let startY = fun (_, _, y, _, _) -> y
let width = fun (_, _, _, w, _) -> w
let height = fun (_, _, _, _, h) -> h
What you probably meant to do is to pass the description tuple into each of these destructuring functions, like so:
let setArray (description: int * int * int * int * int) =
let id = description |> (fun (i, _, _, _, _) -> i)
let startX = description |> (fun (_, x, _, _, _) -> x)
let startY = description |> (fun (_, _, y, _, _) -> y)
let width = description |> (fun (_, _, _, w, _) -> w)
let height = description |> (fun (_, _, _, _, h) -> h)
But there's a much easier way to do this. F# lets you destructure a tuple in the function signature. So you can replace that whole code block with the following:
let setArray (id, startX, startY, width, height) =
That's it! So now your entire function looks like:
let setArray (id, startX, startY, width, height) =
let arrayResult = Array2D.init (width + 1) (height + 1) (fun i j -> if i < width && j < height then id)
arrayresult
And there's one more simplification you can make. Any time you have let x = (some calculation) followed immediately by x as the function's return value, you can get rid of that let and just have the function return (some calculation). Applying that simplification, your function becomes:
let setArray (id, startX, startY, width, height) =
Array2D.init (width + 1) (height + 1) (fun i j -> if i < width && j < height then id)
And you're done! If you really want to specify that id, startX, etc., are all integers, you can do it like this:
let setArray (id : int, startX : int, startY : int, width : int, height : int) =
Array2D.init (width + 1) (height + 1) (fun i j -> if i < width && j < height then id)
Trying to detect the first run of an Iterator protocol.
In the example below I'm trying to start printing Fibonacci series from Zero but it starts from One:
class FibIterator : IteratorProtocol {
var (a, b) = (0, 1)
func next() -> Int? {
(a, b) = (b, a + b)
return a
}
}
let fibs = AnySequence{FibIterator()}
print(Array(fibs.prefix(10)))
What modifications can be made to the above code to detect the first run?
To answer your verbatim question: You can add a boolean variable
firstRun to detect the first call of the next() method:
class FibIterator : IteratorProtocol {
var firstRun = true
var (a, b) = (0, 1)
func next() -> Int? {
if firstRun {
firstRun = false
return 0
}
(a, b) = (b, a + b)
return a
}
}
let fibs = AnySequence { FibIterator() }
print(Array(fibs.prefix(10))) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
But there are more elegant solutions for this problem.
You can “defer” the update of a and b to be done after returning
the current value:
class FibIterator : IteratorProtocol {
var (a, b) = (0, 1)
func next() -> Int? {
defer { (a, b) = (b, a + b) }
return a
}
}
let fibs = AnySequence { FibIterator() }
print(Array(fibs.prefix(10))) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
or – perhaps simpler – change the initial values (using the fact
that the Fibonacci numbers are defined for negative indices as well):
class FibIterator : IteratorProtocol {
var (a, b) = (1, 0) // (Fib(-1), Fib(0))
func next() -> Int? {
(a, b) = (b, a + b)
return a
}
}
let fibs = AnySequence { FibIterator() }
print(Array(fibs.prefix(10))) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Note that if you declare conformance to the Sequence protocol
then you don't need the AnySequence wrapper (there is a default
implementation of makeIterator() for types conforming to
IteratorProtocol). Also value types are generally preferred,
so – unless the reference semantics is needed – you can make it a struct:
struct FibSequence : Sequence, IteratorProtocol {
var (a, b) = (1, 0) // (Fib(-1), Fib(0))
mutating func next() -> Int? {
(a, b) = (b, a + b)
return a
}
}
let fibs = FibSequence()
I have been working on a function for reducing fractions in Swift, and came across the Euclidean algorithm for finding the greatest common factor (http://en.wikipedia.org/wiki/Euclidean_algorithm)
I converted the pseudo code into swift, but yet I am confused how this is going to give me the greatest common factor if it is returning a which I thought was supposed to be the numerator of the fraction. Any help on this would be greatly appreciated. Thanks!
Pseudocode:
function gcd(a, b)
while b ≠ 0
t := b
b := a mod b
a := t
return a
Swift:
var a = 2
var b = 4
func gcd(a: Int, b: Int) -> Int {
var t = 0
while b != 0 {
t = b
let b = a % b
let a = t
}
return a
}
println("\(a)/\(b)")
Console output: 2/4
When you do this
let b = a % b
you are creating another readonly variable b, which has nothing to do with the variable b from the outside scope. You need to remove both lets inside the loop, and make parameters modifiable by declaring them with var, like this:
func gcd(var a: Int, var b: Int) -> Int {
var t = 0
while b != 0 {
t = b
b = a % b
a = t
}
return a
}
You can call your function like this:
let a = 111
let b = 259
println("a=\(a), b=\(b), gcd=\(gcd(a,b))")
This prints a=111, b=259, gcd=37
Taking #dasblinkenlight's answer and getting rid of t by using tuples for parallel assignment yields:
Swift 2.1:
func gcd(var a: Int, var _ b: Int) -> Int {
while b != 0 {
(a, b) = (b, a % b)
}
return a
}
gcd(1001, 39) // 13
var parameters are deprecated in Swift 2.2 and will be removed in Swift 3. So now it becomes necessary to declare a and b as var within the function:
func gcd(a: Int, _ b: Int) -> Int {
var (a, b) = (a, b)
while b != 0 {
(a, b) = (b, a % b)
}
return a
}
Swift 3 version of answer given by Christopher Larsen
func gcd(a: Int, b: Int) -> Int {
if b == 0 { return a }
return gcd(a: b, b: a % b)
}
Can use a recursive method that just keeps calling itself until the GCD is found.
func gcd(a: Int, b: Int) -> Int {
if b == 0 {
return a
}
let remainder: Int = a % b
return gcd(b, b: remainder)
}
and use like so
let gcdOfSomeNums = gcd(28851538, b: 1183019)
//gcdOfSomeNums is 17657