I have this line here, I can not understand the purpose, and the sytacs.
instances = Array(Array(instance.enumerated())[prefixItems.count...])
What does ...
What does [prefixItems.count...] when creating an Array?
How instances can be assigned to a type like: [(Int, Any)].
this is the whole statement, it is syntactically valid:
let instances: [(Int, Any)]
if let prefixItems = schema["prefixItems"]?.array {
guard instance.count > prefixItems.count else {
return AnySequence(EmptyCollection())
}
instances = Array(Array(instance.enumerated())[prefixItems.count...])
} else {
instances = Array(instance.enumerated())
}
but for some reason I need convert Any to DataToValidate, like:
let instances: [(Int, DataToValidate)]
and then I got error:
Cannot assign value of type 'Array<EnumeratedSequence<(JSONArray)>.Element>' (aka 'Array<(offset: Int, element: JSONValue)>') to type '[(Int, DataToValidate)]'
The x... is a one-sided range from x to "as far as possible", i.e., to the end of the array in case of subscripting, as done here.
Array(instance.enumerated()) initializes an array from the enumerated sequence instance, i.e., [(Int, Any)]. The subscript [prefixItems.count...] takes the elements from that array starting from the index prefixItems.count and continuing to the end. The outer Array initializes an array of that sequence.
The types of everything involved above (in practice some will have more specific actual types, but they conform to these):
instance – some Sequence, but we can consider it [Any]
instance.enumerated() – Sequence<(Int, Any)>
Array(instance.enumerated()) – [(Int, Any)]
Array(instance.enumerated())[prefixItems.count...] – Sequence<(Int, Any>)>
Array(Array(instance.enumerated())[prefixItems.count...]) – [(Int, Any)]
P. S. As pointed out in comments, note that the intermediate array and subscripting is unnecessary, and the same outcome can be achieved with Array(instance.enumerated().dropFirst(prefixItems.count)). This also makes the guard unnecessary.
Related
What does Element mean when writing an extension on Array?
like in this example:
extension Array {
func reduce<T>(_ initial: T, combine: (T, Element) -> T) -> T {
var result = initial
for x in self {
result = combine(result, x)
}
return result
}
}
The combine parameter is a function which takes a parameter of type T and Element. The Element is the actual Element of/in the array.
For example, this is an array of Int elements:
let arr = [1,2,5,77]
In reduce, initial is of type T. This is the staring value for the mapping you are about to perform.
In combine, T is like your starting value for each subsequent step of combing the next Element to produce another value of type T which will be used as the next T in combine, and so and so forth until the entire array has been processed.
If you were using a default use of reduce such as:
arr.reduce(0, +)
You can see that in this case, T and Element would both be of the same type, Int.
However, I could have a custom object that the array is of, and my combine is defining how to get the running total. If you had something like this:
struct Thing {
var val1: String
var val2: Int
}
let thingArray = //...define some Things in an array
You could use reduce and define your own combine function to return the sum of all the val2 values. In this case, T would be an Int, and Element would be Thing.
I want to assign the 3 numbers in an array to the 3 elements of a tuple using a for loop. The following function seems to do the trick, but Xcode errors as soon as I try to reference the (seemingly) fully assigned tuple. Am I doing something wrong?
typealias Triple = (Int, Int, Int)
func createTuple() -> Triple
{
var tuple: Triple
let r = [8, 6, 3]
for i in 0..<r.count {
switch(i) {
case 0: tuple.0 = r[i]
case 1: tuple.1 = r[i]
case 2: tuple.2 = r[i]
default : break
}
}
return tuple // Xcode says "Variable 'tuple.0' used before being initialized
}
The problem of determining whether a variable can be guaranteed to have been assigned or not is not decidable for the general case. This can be proven by a transformation on the halting problem. If you want to know more about that, I suggest you post a question on the Computer Science stack exchange site.
The Swift compiler approximates the solution to this problem by handling specific cases. Your case is not one of those handled by the Swift compiler (yet)?
If you just want to assign the triple, you can do this:
typealias Triple = (Int, Int, Int)
func createTriple() -> Triple {
return (8, 6, 3)
}
If you want to assign it from an array, it's a lot simpler without the loop and switch (for the case of just 3 components)
typealias Triple = (Int, Int, Int)
func createTriple(from array: [Int]) -> Triple {
guard array.count > 3 else {
fatalError("Can't create a triple from less than 3 elements")
}
return (array[0], array[1], array[2])
}
Although your code covers all three elements of the tuple, Xcode can't be certain that this is true. Between the loop and the switch, Xcode can't be certain that you're actually assigning to each element in tuple. Maybe the loop won't run enough times, or maybe the switch won't hit every case. You can see that they're all covered, but Xcode isn't quite sure.
You should either (a) assign default values to tuple, or (b) if defaults don't make sense, change Triple to contain three optional values, i.e.
typealias Triple = (Int?, Int?, Int?)
I am a little confused regarding the logic behind the sorted(by:) function in Swift (it was sort() in Swift 2). Take the code below for instance...how does the return type bool yield in the reverse ordering of the numbers?
let aBunchOfNumbers = [1,2,3,4,5,6,7,8,9,10]
let reverseSortClosure: (Int, Int) -> Bool = {
(numberOne: Int, numberTwo: Int) -> Bool in
if numberOne < numberTwo {
return true
}
return false
}
aBunchOfNumbers.sorted(by: reverseSortClosure)
(sort(_:) was renamed sorted(by:) since Swift 3)
From the docs:
https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID95
The sorted(by:) method accepts a closure that takes two arguments of the same type as the array’s contents, and returns a Bool value to say whether the first value should appear before or after the second value once the values are sorted. The sorting closure needs to return true if the first value should appear before the second value, and false otherwise.
That is, the type class for the function that sorted() accepts looks like (T, T) -> Bool, where T is the type of the inout array you want to sort. The function returns true IF the first value of the function should appear before the second value. Swift uses this function to order the list you pass in.
In Swift 2 the sort() method needs to be used as an extension of your array. aBunchOfNumbers.sort(reverseSortClosure) and if you want to reverse it you have to use >.
let aBunchOfNumbers = [1,2,3,4,5,6,7,8,9,10]
let reverseSortClosure: (Int, Int) -> Bool = {
(lhs, rhs) -> Bool in
return lhs > rhs
}
let reversed = aBunchOfNumbers.sort(reverseSortClosure) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
You can also simplify your code as follow:
let reversed = aBunchOfNumbers2.sort(>)
The sort method is sorting the array, so the method needs to know what value goes first, second, third, etc. The value that goes first is the value that is true, and the value which goes second is false. That is why the sort method returns in a bool. Also, your code is really complicated and not at all swifty. This does the same thing, just a lot faster.
let aBunchOfNumbers = [1,2,3,4,5,6,7,8,9,10]
aBunchOfNumbers.sort({
$0 > $1 })
Don't be worried if you're not getting closures at first. They are difficult to understand and the syntax is somewhat unique to swift.
Hope this helps
Perhaps you are learning about closures, but you don't need one here. The sort() function takes a second argument that is a function to compare two values - the function returns true if its first argument appears before the second argument.
In your case, the array contains Int - there is already a perfectly good function to compare Ints - it is >. Use it like this:
1> let result = [1,2,3,4].sort(>)
result: [Int] = 4 values {
[0] = 4
[1] = 3
[2] = 2
[3] = 1
}
In Swift, how is tuple related to function argument?
In the following two examples the function returns the same type even though one takes a tuple while the other takes two arguments. From the caller standpoint (without peeking at the code), there is no difference whether the function takes a tuple or regular arguments.
Is function argument related to tuple in some ways?
e.g.
func testFunctionUsingTuple()->(Int, String)->Void {
func t(x:(Int, String)) {
print("\(x.0) \(x.1)")
}
return t
}
func testFuncUsingArgs()->(Int, String)->Void {
func t(x:Int, y:String) {
print("\(x) \(y)")
}
return t
}
do {
var t = testFunctionUsingTuple()
t(1, "test")
}
do {
var t = testFuncUsingArgs()
t(1, "test")
}
There is also inconsistencies in behavior when declaring tuple in function argument in a regular function (rather than a returned function):
func funcUsingTuple(x:(Int, String)) {
print("\(x.0) \(x.1)")
}
func funcUsingArgs(x:Int, _ y:String) {
print("\(x) \(y)")
}
// No longer works, need to call funcUsingTuple((1, "test")) instead
funcUsingTuple(1, "test")
funcUsingArgs(1, "test3")
UPDATED:
Chris Lattner's clarification on tuple:
"x.0” where x is a scalar value produces that scalar value, due to odd
behavior involving excessive implicit conversions between scalars and
tuples. This is a bug to be fixed.
In "let x = (y)”, x and y have the same type, because (x) is the
syntax for a parenthesis (i.e., grouping) operator, not a tuple
formation operator. There is no such thing as a single-element
unlabeled tuple value.
In "(foo: 42)” - which is most commonly seen in argument lists -
you’re producing a single element tuple with a label for the element.
The compiler is currently trying hard to eliminate them and demote
them to scalars, but does so inconsistently (which is also a bug).
That said, single-element labeled tuples are a thing.
Every function takes exactly one tuple containing the function's arguments. This includes functions with no arguments which take () - the empty tuple - as its one argument.
Here is how the Swift compiler translates various paren forms into internal representations:
() -> Void
(x) -> x
(x, ...) -> [Tuple x ...]
and, if there was a tuple? function, it would return true on: Void, X, [Tuple x ...].
And here is your proof:
let t0 : () = ()
t0.0 // error
let t1 : (Int) = (100)
t1.0 -> 100
t1.1 // error
let t2 : (Int, Int) = (100, 200)
t2.0 -> 100
t2.1 -> 200
t2.2 // error
[Boldly stated w/o a Swift interpreter accessible at the moment.]
From AirSpeedVelocity
But wait, you ask, what if I pass something other than a tuple in?
Well, I answer (in a deeply philosophical tone), what really is a
variable if not a tuple of one element? Every variable in Swift is a
1-tuple. In fact, every non-tuple variable has a .0 property that is
the value of that variable.4 Open up a playground and try it. So if
you pass in a non-tuple variable into TupleCollectionView, it’s legit
for it to act like a collection of one. If you’re unconvinced, read
that justification again in the voice of someone who sounds really
confident.
Remember the 'philosophical tone' as we've reached the 'I say potato; your say potato' phase.
A function in Swift takes a tuple as parameter, which can contain zero or more values. A parameterless function takes a tuple with no value, a function with one parameter takes a tuple with 1 value, etc.
You can invoke a function by passing parameters individually, or by grouping them into an immutable tuple. For example, all these invocations are equivalent:
do {
let t1 = testFunctionUsingTuple()
let t2 = testFuncUsingArgs()
let params = (1, "tuple test")
t1(params)
t1(2, "test")
t2(params)
t2(3, "test")
}
I would like to use more functional programming in Swift. Some of the functions I write could work well on Arrays of various types. I don't want to rewrite the same function with different types (or typealiases.) The pattern how the function would work is often the same, just with different types. So, I tried something like this:
// Add indeces to an array of any type. I.e., returns an array of tuples of the array index and the original element.
func addIndeces<T: AnyObject>(toArray: Array<T>) -> Array<(index: Int, value: T)> {
var arrIndex: [Int] = []
for index in 0...toArray.count {
arrIndex.append(index)
}
return Array(Zip2(arrIndex, toArray))
}
When I call this function
// Note: reminderList is of type [Reminder]
let indexedReminderList = addIndeces(reminderList) as! [(index: Int, reminder: Reminder)]
I get a runtime error: "fatal error: can't unsafeBitCast between types of different sizes"
What am I doing wrong?
The function you are writing already exists – kind of. enumerate "return a lazy SequenceType containing pairs (n, x), where n\ s are consecutive Int\ s starting at zero, and x\ s are the elements of base"
This means you can write your function as:
func addIndices<T>(toArray: [T]) -> [(index: Int, reminder: T)] {
// if you want to label the tuple elements index: and reminder:,
// you still have to use map:
return map(enumerate(toArray)) {
(index: $0, reminder: $1)
}
}
Note, you don’t need to write T: AnyObject unless you specifically want to prevent this function from accepting anything other than arrays of classes (as that's what AnyObject is - the protocol that only classes, not structs or enums, conform to).
Note, enumerate only works for integer-indexed collections. To make it more general, you could write zip(indices(someCollection),someCollection)). indices returns a range of all the indices of any collection, so is equivalent to someCollection.startIndex..<someCollection.endIndex.
You want to cast the tuple Array of type [(index: Int, value: T)] where T is of type Reminder to a tuple Array of type [(index: Int, reminder: Reminder)]. So you can see that the tuples have different element names (value and reminder) where both have different byte sizes - therefore the error.
So you should take the same element names for both tuples.
If you want to change the element names:
let newTupleArray = oldTupleArray.map{ (newElementName0: $0.0, newElementName1: $0.1) }
The range 0...toArray.count consist all elements form 0 to the count of toArray, including the count value, and in your case arrIndex will always have one more element then toArray. Thats what is causing different size error. To fix it replace your range with this one 0..<toArray.count