I have a theoretical question, I did not find related topics.
At some point, I decided that it would be nice to have a small extension for an array:
var array = [Int]()
array += 1
The code is quite simple:
extension Array {
mutating static func +=(lhs: Array, rhs: Element) {
lhs.append(rhs)
}
}
To achieve this we align with two factors that make perfect sense to me:
Array is a struct and this operation requires a mutation
Infix operator reload requires a static function
Unfortunately, it is impossible due Swift does not allow mutating functions to be static. And this is the part I don't quite understand.
Your += mutates the first argument, not the Array type.
Therefore it must not be declared mutating (which makes no
sense for a static method because you cannot mutate the type), but the first parameter must be inout:
extension Array {
static func +=(lhs: inout Array, rhs: Element) {
lhs.append(rhs)
}
}
var array = [Int]()
array += 1
print(array) // [1]
Because mutating doesn't mean "mutates anything", but rather, "mutates self". Your function attempts to mutate lhs, not self.
Your current code won't work because lhs is being passed by value. The lhs parameter is a local copy of whatever argument the caller supplied to it, thus any changes your function makes will be local to the function and won't persist. You'll need to instead have lhs be passed by reference, by delcaring it as a inout Array.
By using static keyword before method name means, we call method by struct/class name (Not by an object) so we don't have any object here.
By using mutating keyword, we are mutating 'self' object.
So while using static we don't have any object to mutate.
Related
I need to manipulate data inside a byte array using swift. The compiler will not permit the following:
var a:[UInt8] = [1,2,3,4,5]
func tt(a:[UInt8]) {
a[2]=10
}
tt(a:a)
The compiler complains:
Cannot assign through subscript: 'a' is a 'let' constant
What is the correct way to create functions that can modify large byte arrays. (They are very large)
I am aware there are Unsafe pointer options, but I am trying various types of unsafe pointer parameters and none of them seem to work or they report even more obscure compiler errors, so I thought I would ask here. i.e.
func tt(a:UnsafePointer<[UInt8]>) {
a[2]=a[3]
}
func tt(a:UnsafeMutablePointer<[UInt8]>) {
a[2]=a[3]
}
func tt(a:[UInt8]) {
a[2]=10
}
In above function, the function parameter a is a let constant, you can't change it inside the function.
You need to use inout parameter to be able to modify this.
var a: [UInt8] = [1,2,3,4,5]
func tt(a: inout [UInt8]) {
a[2] = 10
}
tt(a: &a)
Each value passed inside a function parameter is a constant, to be able to mutate it you need to sign it as inout
func tt(a: inout [UInt8]){
a[2]=10
}
I'm aware of Swift's higher-order functions like Map, Filter, Reduce and FlatMap, but I'm not aware of any like 'All' or 'Any' which return a boolean that short-circuit on a positive test while enumerating the results.
For instance, consider you having a collection of 10,000 objects, each with a property called isFulfilled and you want to see if any in that collection have isFulfilled set to false. In C#, you could use myObjects.Any(obj -> !obj.isFulfilled) and when that condition was hit, it would short-circuit the rest of the enumeration and immediately return true.
Is there any such thing in Swift?
Sequence (and in particular Collection and Array) has a (short-circuiting) contains(where:) method taking a boolean predicate as argument. For example,
if array.contains(where: { $0 % 2 == 0 })
checks if the array contains any even number.
There is no "all" method, but you can use contains() as well
by negating both the predicate and the result. For example,
if !array.contains(where: { $0 % 2 != 0 })
checks if all numbers in the array are even. Of course you can define a custom extension method:
extension Sequence {
func allSatisfy(_ predicate: (Iterator.Element) -> Bool) -> Bool {
return !contains(where: { !predicate($0) } )
}
}
If you want to allow "throwing" predicates in the same way as the
contains method then it would be defined as
extension Sequence {
func allSatisfy(_ predicate: (Iterator.Element) throws -> Bool) rethrows -> Bool {
return try !contains(where: { try !predicate($0) } )
}
}
Update: As James Shapiro correctly noticed, an allSatisfy method has been added to the Sequence type in Swift 4.2 (currently in beta), see
SE-0027 Add an allSatisfy algorithm to Sequence
(Requires a recent 4.2 developer snapshot.)
One other thing that you can do in Swift that is similar to "short circuiting" in this case is to use the lazy property of a collection, which would change your implementation to something like this:
myObjects.lazy.filter({ !$0.isFulfilled }).first != nil
It's not exactly the same thing you're asking for, but might help provide another option when dealing with these higher-order functions. You can read more about lazy in Apple's docs. As of this edit the docs contain the following:
var lazy: LazyCollection> A view onto this collection
that provides lazy implementations of normally eager operations, such
as map and filter.
var lazy: LazySequence> A sequence containing the same
elements as this sequence, but on which some operations, such as map
and filter, are implemented lazily.
If you had all the objects in that array, they should conform to some protocol, which implements the variable isFulfilled... as you can see, you could make these objects confrom to (let's call it fulFilled protocol)... Now you can cast that array into type [FulfilledItem]... Now you can continue as usually
I am pasting code here for your better understanding:
You see, you cannot extend Any or AnyObject, because AnyObject is protocol and cannot be extended (intended by Apple I guess), but you can ,,sublass" the protocol or as you like to call it professionally - Make protocol inheriting from AnyObject...
protocol FulfilledItem: AnyObject{
var isFulfilled: Bool {get set}
}
class itemWithTrueValue: FulfilledItem{
var isFulfilled: Bool = true
}
class itemWithFalseValue: FulfilledItem{
var isFulfilled: Bool = false
}
var arrayOfFulFilled: [FulfilledItem] = [itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue(),itemWithFalseValue()]
let boolValue = arrayOfFulFilled.contains(where: {
$0.isFulfilled == false
})
Now we've got ourselves a pretty nice looking custom protocol inheriting all Any properties + our beautiful isFulfilled property, which we will handle now as usually...
According to apple docs:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID342
AnyObject is only for reference types (classes), Any is for both value and reference types, so I guess it is prefered to inherit AnyObject...
Now you cast instead AnyObject into Array the protocol Item FulfilledItem and you will have beautiful solution (don't forget every item to conform to that protocol and set the value...)
Wish happy coding :)
Currently I have a class of generic type, and I want to make the object of this class searchable via
contains()
method for an array of those objects, by making the class conform to Hashable protocol and provide a hash value for each object. Now my problem is I have objects with exactly the same properties, and it seems that the array cannot really distinguish them (my current approach is to use one of the properties' hash value as the hash value for the class, and the
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
function is done by comparing the hash value). I have tried to use a static property like "id", but for generic types static properties are not supported.
How should I define the hash value such that different objects with the same properties can still be differentiated?
EDIT: I'm making it conform to Hashable directly because it's also used as keys in dict in other parts of the program, since Hashable already conforms to Equatable.
My current approach is to use one of the properties' hash value as the
hash value for the class, and the
== <T> (lhs: ClassA<T>, rhs: ClassA<T>) -> Bool
function is done by comparing the hash value
That's not how the == and hashValue relationship works – don't do this. What if you get a hash collision? Two different instances with different properties could compare equal.
You should instead implement == to actually compare the properties of two instances. == should return true if two given instances have equivalent properties. The hashValues of two instances should be equivalent if they compare equal with ==.
Now, it might well be the case that you cannot do this comparison unless T is Equatable. One solution to this is to not conform ClassA to Equatable, but instead just overload == for when T is Equatable, such as:
func == <T : Equatable>(lhs: ClassA<T>, rhs: ClassA<T>) -> Bool {
// stub: do comparison logic
}
You can now just use Sequence's contains(where:) method in conjunction with the == overload in order to check if a given instance is in the array:
var array = [ClassA("foo")] // assuming ClassA has an init(_: T) and a suitable ==
// implementation to compare that value
let someInstanceToFind = ClassA("foo")
print(array.contains { $0 == someInstanceToFind }) // true
And if you want ClassA to have a hashValue, then simply write an extension that defines a hashValue when T is Hashable:
extension ClassA where T : Hashable {
var hashValue: Int {
return 0 // to do: implement hashValue logic
}
}
Unfortunately, this does mean that ClassA won't explicitly conform to Hashable when T does – but it will have a hashValue and == implementation. SE-0143: Conditional conformances will change this by allowing explicit conformance to protocols if a given where clause if satisfied, but this is yet to be implemented.
If you need explicit conformance to Hashable (such as for using instances of your class in a Set or as Dictionary keys) – then one solution is to create a wrapper type:
struct HashableClassA<T : Hashable> : Hashable {
var base: ClassA<T>
init(_ base: ClassA<T>) {
self.base = base
}
static func ==(lhs: HashableClassA, rhs: HashableClassA) -> Bool {
return lhs.base == rhs.base
}
var hashValue: Int {
return base.hashValue
}
}
Now you just have to wrap ClassA<T> instances in a HashableClassA instance before adding to a Set or Dictionary.
Just realized there is a simple way for achieving the Equatable in
contains()
method: use
return lhs === rhs
in the == function such that objects are compared directly. It's working in this way now.
I am trying to run this code, and this error is being triggered
Cannot invoke 'append' with an argument list of type '(Int)'
What am I doing wrong?
extension Array {
mutating func random100() {
for _ in 0 ... 99 {
self.append(Int(arc4random() % 10)) // Cannot invoke 'append' with an argument list of type '(Int)'
}
}
}
You must constraint your extension to Int types :
extension RangeReplaceableCollection where Iterator.Element == Int {
mutating func random100() {
for _ in 1...100 {
append(Int(arc4random_uniform(10)))
}
}
}
And as you cannot directly constraint Array, you must constraint the protocol where the append method is defined.
Then you can use it on any array of Int:
var myArray = [3,5,6]
myArray.random100()
What do you really want? If you want a method that gives you a hundred random Ints, you’d better create a static method on Array. Mutating any array in general does not make sense – what if it’s a list of strings?
Take a look at arc4random_uniform to avoid modulo bias. I don’t know if the bias would be apparent in this case, but it’s a good practice to use arc4random_uniform anyway.
Why should we be required to specify mutating for a function in a struct explicitly? Can't it be inferred automatically from the function body while compiling? If it can't, can you please give me an example where it can't...
There are some special examples when the compiler cannot infer mutability because it's the programmer decision to say whether the behavior should be considered as mutable.
For example, when the struct is backed up by a class:
struct MyArray<T : AnyObject> {
let buffer: NSMutableArray = NSMutableArray()
func append(item: T) {
buffer.addObject(item)
}
}
let myArray = MyArray<NSString>()
myArray.append("something")
Note that the append function is modifying the struct but it doesn't have the mutating attribute, so you can use it even on constant variables. If you want to prevent that, you need to add the mutating attribute.
I didn't choose the array example accidentally. The dictionaries and arrays in Swift are backed up by classes and they had to choose which methods would be mutating and which wouldn't.