Related
I'd like to access the raw bytes in Data as an array of numeric types for quick parsing. I have lots of data to parse, so I'm looking for efficiency. Data is a mix of shorts, ints, longs, and doubles.
The below code seems to work and be fast, but I'm getting deprecated warning: 'withUnsafeBytes' is deprecated
I can't figure out the updated way to treat the underlying bytes as an array of numbers for quick lookup.
var data: Data = ...
// create "virtual" arrays of different types to directly access bytes (without copying bytes)
let ushortArray: [UInt16] = data.withUnsafeBytes {
[UInt16](UnsafeBufferPointer(start: $0, count: data.count/2))
}
let intArray: [Int32] = data.withUnsafeBytes {
[Int32](UnsafeBufferPointer(start: $0, count: data.count/4))
}
// Access data simply
var i1: UInt16 = ushortArray[0]
var i2: Int32 = intArray[1]
P.S. I'm not concerned with big/little endian
If you want to load it as an Array what you need is to use UnsafeRawBufferPointer bindMemorymethod to the type you want. you can also extend ContiguousBytes to make a generic method and simplify your syntax:
Note: if you are planing to get a subsequence of your data make sure to do NOT subscript the data to avoid misaligned errors. You need to use Data subdata method instead.
extension ContiguousBytes {
func objects<T>() -> [T] { withUnsafeBytes { .init($0.bindMemory(to: T.self)) } }
var uInt16Array: [UInt16] { objects() }
var int32Array: [Int32] { objects() }
}
extension Array {
var data: Data { withUnsafeBytes { .init($0) } }
}
Usage:
let uInt16Array: [UInt16] = [.min, 1, 2, 3, .max] // [0, 1, 2, 3, 65535]
let int32Array: [Int32] = [.min, 1, 2, 3, .max] // [-2147483648, 1, 2, 3, 2147483647]
let uInt16ArrayData = uInt16Array.data // 10 bytes
let int32ArrayData = int32Array.data // 20 bytes
let uInt16ArrayLoaded = uInt16ArrayData.uInt16Array // [0, 1, 2, 3, 65535]
let int32ArrayLoaded = int32ArrayData.int32Array // [-2147483648, 1, 2, 3, 2147483647]
// Access data simply
let i1 = uInt16ArrayLoaded[0] // UInt16 0
let i2 = int32ArrayLoaded[0] // Int32 -2147483648
let i3 = uInt16ArrayLoaded[4] // UInt16 65535
let i4 = int32ArrayLoaded[4] // Int32 2147483647
I want to 'pretty print' an Array of Ints as hex in Swift. I have the below working as an extension of Array. But I could only get a method arr.toHex() to work, and not just arr.toHex as I wanted to do. I am wondering if it is possible at all.
Second to that, in reality I would like to do arr as Hex and define Hex in some way. But no idea how.
Thirdly, in my working example I needed to cast to Int to get this to work. But sometimes it will be an Array(Uint8) and this casting doesn't work. Should I have used an optional?
I am using the latest Swift.
let arr = [255, 255, 128, 64, 16]
extension Array {
func asHex() -> String {
var res = "0x"
for el in self {
res.append(String(format:"%02X", el as! Int) + " ")
}
return res
}
}
print(arr.asHex())
// output: 0xFF FF 80 40 10
// would wonder whether the looping function could make into a calculated variable like this:
print(arr.asHex)
// should generate same output
// and ultimately:
print(arr as Hex)
// I have no idea how to do this
Update:
Combining the answers I got very close, but I cannot get it working for [Int] and [UInt8] in one 'one liner' as I planned. I got to this to cater for both types (say Element in [Int, UInt8]:
extension Array where Element == Int {
var asHex: String { "0x" + self.map { String(format: "%02X", $0) }.joined(separator: " ")
}
}
extension Array where Element == UInt8 {
var asHex: String { "0x" + self.map { String(format: "%02X", $0) }.joined(separator: " ")
}
}
print( [Int(12), Int(255)].asHex ) //0x0C FF
print( [UInt8(12), UInt8(255)].asHex ) //0x0C FF
I now understand Int and UInt8 are BinaryIntegers and that the $0 in the format of the string is a CVarArg. For that reason I could not use extension Array where Element: BinaryInteger since the $0 complained. There must be a way however. So close (and learned a lot)
To convert your function into a computed property, use this:
extension Array where Element == Int {
var asHex: String {
return self.map { String(format: "%02X", $0) }.joined(separator: " ")
}
}
Don't pursue the arr as Hex route, casting is not the right tool for this problem.
`print(arr.asHex)' won't work as you are telling it to print the method itself, not to execute the method and print the output.
If you want to lose the method signature, do it as a computed property:
extension Array where Element == Int{
var asHex: String {
var res = "0x"
for el in self {
res.append(String(format:"%02X", el as! Int) + " ")
}
return res
}
}
let arr = [123,456,23]
print(arr.asHex)
// "0x7B 1C8 17"
also worth constraining the extension as above so it only works for an array of Int
Don't worry too much about as Hex, it won't bring you too many satisfactory results in Swift. About the other two bullet points, you could simply extend all Sequence types that are either Int8 or UInt8 based with an asHex functionality:
func hexReduce(_ acc: String, _ el: CVarArg) -> String {
acc + String(format: "%02X ", el)
}
extension Sequence where Self.Element == Int8 {
var asHex: String { reduce("0x", hexReduce) }
}
extension Sequence where Self.Element == UInt8 {
var asHex: String { reduce("0x", hexReduce) }
}
let arr1: [Int8] = [1, 2, 3]
let arr2: [UInt8] = [1, 2, 3]
print(arr1.asHex) // 0x01 02 03
print(arr2.asHex) // 0x01 02 03
I've actually googled this extensively, within stackoverflow and elsewhere.
Most questions are about [UInt8] to String or [UInt8] to type_a (not array).
To clarify, I'd like to take an array of type_a. Get its pointer and tell swift to treat the next n iterations of type_b (size_of) as array of type_b.
I've tried variations of https://stackoverflow.com/a/26954091/5276890 which didn't work. A comment there led me to https://stackoverflow.com/a/42255468/5276890.
withMemoryRebound seems like the right way but I couldn't find the right invocation.
Here's a sample code of what I'm doing instead to convert [UInt8] to [UInt32.bigEndian], both to clarify and in case it's useful (not likely)
var intData = [UInt32]()
let M = UInt32(256*256*256)
var m = M
var bigE:UInt32 = 0
for i in 0..<data.count {
bigE += UInt32(data[i]) * m
if m == 1 {
intData.append(bigE)
bigE = 0
m = M
} else {
m = m/256
}
}
<disclaimer+rant>
I have to admit I never could figure out the whole closures+withUnsafe* syntax and mostly used patterns online and modified them. I'd spend the time learning this, just as soon as the language authors decide and settle down on one specific syntax :(
</disclaimer+rant>
Use withUnsafeBufferPointer to get a pointer to the element
storage of the source array.
Use withMemoryRebound to "reinterpret" that pointer as pointing
to elements of the target type.
Use Array(UnsafeBufferPointer(...) to create an array of the
target type.
Example:
let source: [UInt16] = [1, 2, 3, 4]
let dest = source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 2) {
Array(UnsafeBufferPointer(start: $0, count: 2))
}
}
print(dest) // [131073, 262147]
Or as a generic function:
func convertArray<S, T>(_ source: [S], to: T.Type) -> [T] {
let count = source.count * MemoryLayout<S>.stride/MemoryLayout<T>.stride
return source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: T.self, capacity: count) {
Array(UnsafeBufferPointer(start: $0, count: count))
}
}
}
Example:
let source: [UInt16] = [1, 2, 3, 4]
let dest = convertArray(source, to: UInt32.self)
print(dest) // [131073, 262147]
If you only need a (temporary) view on the array storage interpreted
in another type then you can avoid the Array creation
and use the UnsafeBufferPointer (which is a Collection and
has array-like methods) without copying the data:
source.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: UInt32.self, capacity: 2) {
let u32bufptr = UnsafeBufferPointer(start: $0, count: 2)
// ... Operate on u32bufptr ...
for elem in u32bufptr { print(elem) }
}
}
I'd like a function runningSum on an array of numbers a (or any ordered collection of addable things) that returns an array of the same length where each element i is the sum of all elements in A up to an including i.
Examples:
runningSum([1,1,1,1,1,1]) -> [1,2,3,4,5,6]
runningSum([2,2,2,2,2,2]) -> [2,4,6,8,10,12]
runningSum([1,0,1,0,1,0]) -> [1,1,2,2,3,3]
runningSum([0,1,0,1,0,1]) -> [0,1,1,2,2,3]
I can do this with a for loop, or whatever. Is there a more functional option? It's a little like a reduce, except that it builds a result array that has all the intermediate values.
Even more general would be to have a function that takes any sequence and provides a sequence that's the running total of the input sequence.
The general combinator you're looking for is often called scan, and can be defined (like all higher-order functions on lists) in terms of reduce:
extension Array {
func scan<T>(initial: T, _ f: (T, Element) -> T) -> [T] {
return self.reduce([initial], combine: { (listSoFar: [T], next: Element) -> [T] in
// because we seeded it with a non-empty
// list, it's easy to prove inductively
// that this unwrapping can't fail
let lastElement = listSoFar.last!
return listSoFar + [f(lastElement, next)]
})
}
}
(But I would suggest that that's not a very good implementation.)
This is a very useful general function, and it's a shame that it's not included in the standard library.
You can then generate your cumulative sum by specializing the starting value and operation:
let cumSum = els.scan(0, +)
And you can omit the zero-length case rather simply:
let cumSumTail = els.scan(0, +).dropFirst()
Swift 4
The general sequence case
Citing the OP:
Even more general would be to have a function that takes any sequence
and provides a sequence that's the running total of the input
sequence.
Consider some arbitrary sequence (conforming to Sequence), say
var seq = 1... // 1, 2, 3, ... (CountablePartialRangeFrom)
To create another sequence which is the (lazy) running sum over seq, you can make use of the global sequence(state:next:) function:
var runningSumSequence =
sequence(state: (sum: 0, it: seq.makeIterator())) { state -> Int? in
if let val = state.it.next() {
defer { state.sum += val }
return val + state.sum
}
else { return nil }
}
// Consume and print accumulated values less than 100
while let accumulatedSum = runningSumSequence.next(),
accumulatedSum < 100 { print(accumulatedSum) }
// 1 3 6 10 15 21 28 36 45 55 66 78 91
// Consume and print next
print(runningSumSequence.next() ?? -1) // 120
// ...
If we'd like (for the joy of it), we could condense the closure to sequence(state:next:) above somewhat:
var runningSumSequence =
sequence(state: (sum: 0, it: seq.makeIterator())) {
(state: inout (sum: Int, it: AnyIterator<Int>)) -> Int? in
state.it.next().map { (state.sum + $0, state.sum += $0).0 }
}
However, type inference tends to break (still some open bugs, perhaps?) for these single-line returns of sequence(state:next:), forcing us to explicitly specify the type of state, hence the gritty ... in in the closure.
Alternatively: custom sequence accumulator
protocol Accumulatable {
static func +(lhs: Self, rhs: Self) -> Self
}
extension Int : Accumulatable {}
struct AccumulateSequence<T: Sequence>: Sequence, IteratorProtocol
where T.Element: Accumulatable {
var iterator: T.Iterator
var accumulatedValue: T.Element?
init(_ sequence: T) {
self.iterator = sequence.makeIterator()
}
mutating func next() -> T.Element? {
if let val = iterator.next() {
if accumulatedValue == nil {
accumulatedValue = val
}
else { defer { accumulatedValue = accumulatedValue! + val } }
return accumulatedValue
}
return nil
}
}
var accumulator = AccumulateSequence(1...)
// Consume and print accumulated values less than 100
while let accumulatedSum = accumulator.next(),
accumulatedSum < 100 { print(accumulatedSum) }
// 1 3 6 10 15 21 28 36 45 55 66 78 91
The specific array case: using reduce(into:_:)
As of Swift 4, we can use reduce(into:_:) to accumulate the running sum into an array.
let runningSum = arr
.reduce(into: []) { $0.append(($0.last ?? 0) + $1) }
// [2, 4, 6, 8, 10, 12]
By using reduce(into:_:), the [Int] accumulator will not be copied in subsequent reduce iterations; citing the Language reference:
This method is preferred over reduce(_:_:) for efficiency when the
result is a copy-on-write type, for example an Array or a
Dictionary.
See also the implementation of reduce(into:_:), noting that the accumulator is provided as an inout parameter to the supplied closure.
However, each iteration will still result in an append(_:) call on the accumulator array; amortized O(1) averaged over many invocations, but still an arguably unnecessary overhead here as we know the final size of the accumulator.
Because arrays increase their allocated capacity using an exponential
strategy, appending a single element to an array is an O(1) operation
when averaged over many calls to the append(_:) method. When an array
has additional capacity and is not sharing its storage with another
instance, appending an element is O(1). When an array needs to
reallocate storage before appending or its storage is shared with
another copy, appending is O(n), where n is the length of the array.
Thus, knowing the final size of the accumulator, we could explicitly reserve such a capacity for it using reserveCapacity(_:) (as is done e.g. for the native implementation of map(_:))
let runningSum = arr
.reduce(into: [Int]()) { (sums, element) in
if let sum = sums.last {
sums.append(sum + element)
}
else {
sums.reserveCapacity(arr.count)
sums.append(element)
}
} // [2, 4, 6, 8, 10, 12]
For the joy of it, condensed:
let runningSum = arr
.reduce(into: []) {
$0.append(($0.last ?? ($0.reserveCapacity(arr.count), 0).1) + $1)
} // [2, 4, 6, 8, 10, 12]
Swift 3: Using enumerated() for subsequent calls to reduce
Another Swift 3 alternative (with an overhead ...) is using enumerated().map in combination with reduce within each element mapping:
func runningSum(_ arr: [Int]) -> [Int] {
return arr.enumerated().map { arr.prefix($0).reduce($1, +) }
} /* thanks #Hamish for improvement! */
let arr = [2, 2, 2, 2, 2, 2]
print(runningSum(arr)) // [2, 4, 6, 8, 10, 12]
The upside is you wont have to use an array as the collector in a single reduce (instead repeatedly calling reduce).
Just for fun: The running sum as a one-liner:
let arr = [1, 2, 3, 4]
let rs = arr.map({ () -> (Int) -> Int in var s = 0; return { (s += $0, s).1 } }())
print(rs) // [1, 3, 6, 10]
It does the same as the (updated) code in JAL's answer, in particular,
no intermediate arrays are generated.
The sum variable is captured in an immediately-evaluated closure returning the transformation.
If you just want it to work for Int, you can use this:
func runningSum(array: [Int]) -> [Int] {
return array.reduce([], combine: { (sums, element) in
return sums + [element + (sums.last ?? 0)]
})
}
If you want it to be generic over the element type, you have to do a lot of extra work declaring the various number types to conform to a custom protocol that provides a zero element, and (if you want it generic over both floating point and integer types) an addition operation, because Swift doesn't do that already. (A future version of Swift may fix this problem.)
Assuming an array of Ints, sounds like you can use map to manipulate the input:
let arr = [0,1,0,1,0,1]
var sum = 0
let val = arr.map { (sum += $0, sum).1 }
print(val) // "[0, 1, 1, 2, 2, 3]\n"
I'll keep working on a solution that doesn't use an external variable.
I thought I'd be cool to extend Sequence with a generic scan function as is suggested in the great first answer.
Given this extension, you can get the running sum of an array like this: [1,2,3].scan(0, +)
But you can also get other interesting things…
Running product: array.scan(1, *)
Running max: array.scan(Int.min, max)
Running min: array.scan(Int.max, min)
Because the implementation is a function on Sequence and returns a Sequence, you can chain it together with other sequence functions. It is efficient, having linear running time.
Here's the extension…
extension Sequence {
func scan<Result>(_ initialResult: Result, _ nextPartialResult: #escaping (Result, Self.Element) -> Result) -> ScanSequence<Self, Result> {
return ScanSequence(initialResult: initialResult, underlying: self, combine: nextPartialResult)
}
}
struct ScanSequence<Underlying: Sequence, Result>: Sequence {
let initialResult: Result
let underlying: Underlying
let combine: (Result, Underlying.Element) -> Result
typealias Iterator = ScanIterator<Underlying.Iterator, Result>
func makeIterator() -> Iterator {
return ScanIterator(previousResult: initialResult, underlying: underlying.makeIterator(), combine: combine)
}
var underestimatedCount: Int {
return underlying.underestimatedCount
}
}
struct ScanIterator<Underlying: IteratorProtocol, Result>: IteratorProtocol {
var previousResult: Result
var underlying: Underlying
let combine: (Result, Underlying.Element) -> Result
mutating func next() -> Result? {
guard let nextUnderlying = underlying.next() else {
return nil
}
previousResult = combine(previousResult, nextUnderlying)
return previousResult
}
}
One solution using reduce:
func runningSum(array: [Int]) -> [Int] {
return array.reduce([], combine: { (result: [Int], item: Int) -> [Int] in
if result.isEmpty {
return [item] //first item, just take the value
}
// otherwise take the previous value and append the new item
return result + [result.last! + item]
})
}
I'm very late to this party. The other answers have good explanations. But none of them have provided the initial result, in a generic way. This implementation is useful to me.
public extension Sequence {
/// A sequence of the partial results that `reduce` would employ.
func scan<Result>(
_ initialResult: Result,
_ nextPartialResult: #escaping (Result, Element) -> Result
) -> AnySequence<Result> {
var iterator = makeIterator()
return .init(
sequence(first: initialResult) { partialResult in
iterator.next().map {
nextPartialResult(partialResult, $0)
}
}
)
}
}
extension Sequence where Element: AdditiveArithmetic & ExpressibleByIntegerLiteral {
var runningSum: AnySequence<Element> { scan(0, +).dropFirst() }
}
let sortedNumbers = numbers.sort { $0 > $1 }
print(sortedNumbers)
Can anyone explain, what $0 and $1 means in swift?
More Sample
array.forEach {
actions.append($0)
}
$0 is the first parameter passed into the closure. $1 is the second parameter, etc. That closure you showed is shorthand for:
let sortedNumbers = numbers.sort { (firstObject, secondObject) in
return firstObject > secondObject
}
TL;DR
Swift 5.8
$0 and $1 are closure’s first and second Shorthand Argument Names (SAN for short) or implicit parameter names, if you like. The shorthand argument names are automatically provided by Swift. The first argument is referenced by $0, the second argument is referenced by $1, the third one by $2, and so on.
As you know, a Closure is a self-contained block of functionality (a function without name) that can be passed around and used in your code. Closure has different names in other programming languages as well as slight differences in meaning – it's Lambda in Python and Kotlin, or it's Block in C and Objective-C.
Shortening a closure
let coffee: [String] = ["Cappuccino", "Espresso", "Latte", "Ristretto"]
1. Regular method as argument of a Higher Order Function
func backward(_ n1: String, _ n2: String) -> Bool {
return n1 > n2
}
var reverseOrder = coffee.sorted(by: backward)
/* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */
2. Full closure expression
reverseOrder = coffee.sorted {
(n1: String, n2: String) -> Bool in return n1 > n2
}
3. Inferring Type from context in a closure with an implicit return
reverseOrder = coffee.sorted { n1, n2 in n1 > n2 }
4. Shorthand Argument Names
reverseOrder = coffee.sorted { $0 > $1 }
/* $0 and $1 are closure’s first and second String arguments. */
5. Operator methods
reverseOrder = coffee.sorted(by: >)
/* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */
Higher Order Function with closure
let companies = ["bmw", "kfc", "ibm", "htc"]
let uppercased = companies.map {
(item: String) -> String in return item.uppercased()
}
print(uppercased)
/* RESULT: ["BMW", "KFC", "IBM", "HTC"] */
Shorthand Argument Name $0
let uppercased = companies.map { $0.uppercased() }
print(uppercased)
/* RESULT: ["BMW", "KFC", "IBM", "HTC"] */
Full closure expression with remainder operator (a.k.a. modulo)
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let filteredNumbers = numbers.filter {
(arg: Int) -> Bool in return (arg % 2) == 0
}
print(filteredNumbers)
/* RESULT: [2, 4, 6, 8, 10] */
Shorthand Argument Name with remainder operator
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let filteredNumbers = numbers.filter { ($0 % 2) == 0 }
print(filteredNumbers)
/* RESULT: [2, 4, 6, 8, 10] */
SAN in variadic functions
Variadic functions are ones that accept any number of parameters. One of the most famous variadic methods is print(...). Shorthand Argument Names are perfect for variadic functions.
fileprivate func dessert(_ fruits: String...) -> Bool {
return fruits.contains { $0 == "Apple" }
}
let contains = dessert("Mango", "Durian", "Papaya")
print(contains)
/* RESULT: false */
$0 for existential Sequence type
Here, allSatisfy() method indicates whether every element of a sequence satisfies a given predicate or not. The Any type represents values of any type. Existential any type is used for storing an any kind of value which conforms to a specific protocol. Also, I used here a shorthand syntax for optional binding (shadowing an existing constant).
let inputData: [Any]? = [1, 2, 3, "Hello"]
func satisfiesOrNot(_ inputData: any Sequence) -> Bool {
inputData.allSatisfy { $0 is Int }
}
if let inputData {
satisfiesOrNot(inputData)
}
/* RESULT: false */
Repeating $0
let cubedNumber = { $0 * $0 * $0 } (25)
print(cubedNumber)
/* RESULT: 25^3 = 15625 */
$0 in closure capturing
Swift method can return a closure, that is capable of capturing some value.
func trainer(_ said: String) -> ((String) -> String) {
return {
return "¡\(said) \($0)!"
}
}
let announcement = trainer("Bienvenido a mis cursos")
announcement("RealityKit 2023")
/* RESULT: "¡Bienvenido a mis cursos RealityKit 2023!" */
Shorthand Argument Names $0, $1, $2
let math: (Int8, Int8, Int8) -> Int8 = { $0 + $1 - $2 }
func feedClosure() -> (Int8, Int8, Int8) -> Int8 {
return math
}
feedClosure()(10, 20, 100)
/* RESULT: (10 + 20 - 100) = -70 */
SANs $0, $1, $2, $3, $4
let factorial = { $0 * $1 * $2 * $3 * $4 } (1, 2, 3, 4, 5)
print(factorial)
/* RESULT: 5! = 120 */
Key path expression
Starting from Swift 5.2+ you can access parameters of every instance via key path expression:
struct Lighter {
let manufacturer: String
let refillable: Bool
}
let zippo = Lighter(manufacturer: "Zippo", refillable: true)
let cricket = Lighter(manufacturer: "Cricket", refillable: false)
let lighters: [Lighter] = [zippo, cricket]
let refillableOnes = lighters.map(\.refillable)
print(refillableOnes)
/* RESULT: [true, false] */
Of course, you can alternatively use a familiar syntax:
Regular syntax – $0.property:
let refillableOnes = lighters.map { $0.refillable }
print(refillableOnes)
/* RESULT: [true, false] */
SAN with a subscript
(If you want to know how to implement negative indexing in arrays, read this post please.)
let arrays: [[String]] = [["Hello","Hola","你好"], ["world","mundo","世界"]]
let helloWorld = arrays.compactMap { $0[0] }
print(helloWorld)
/* RESULT: ["Hello", "world"] */
One more example with key shortcut (Swift 5.6) and a subscript:
let dictionaries: [[_ : Any?]] = [[1: "x"], [2: nil], [3: true]]
let values = dictionaries.compactMap { $0[$0.startIndex].value }
print(values)
/* RESULT: ["x", true] */
Or look at the example of unordered set:
let collection: Set<String> = ["One", "", "Three"]
collection.map {
switch $0.isEmpty {
case true:
print("Empty")
case false:
print("Element \($0) isn't empty")
}
}
/* RESULT: "Element Three isn't empty" */
/* "Empty" */
/* "Element One isn't empty" */
SAN in a completion handler
let completionHandler: ((Bool) -> Void)? = {
if $0 {
print("It is true, sister...")
} else {
print("False")
}
}
completionHandler?(true)
/* RESULT: It is true, sister... */
Regular syntax, however, is as following:
let completionHandler: ((Bool) -> Void)? = { sayTheTruth in
if sayTheTruth {
print("It is true, sister...")
} else {
print("False")
}
}
completionHandler?(false)
/* RESULT: False */
SAN in ForEach structure in SwiftUI
let columns: [GridItem] = Array(repeating: .init(.fixed(70)), count: 5)
var body: some View {
ScrollView {
LazyVGrid(columns: columns) {
ForEach((1...10), id: \.self) {
Text("\($0)").frame(maxWidth: .infinity)
}
}
}
}
/* RESULT: 1 2 3 4 5 */
/* 6 7 8 9 10 */
Operator method vs SAN
Operator Method:
let records: [Int] = [110, 108, 107, 109, 108]
public func averageSpeed(records: [Int]) throws -> Int {
let average = records.reduce(0, +) / records.count
return average
}
try averageSpeed(records: records)
/* RESULT: 108 */
Shorthand Argument Names $0 and $1:
public func averageSpeed(records: [Int]) throws -> Int {
let average = records.reduce(0) { $0 + $1 } / records.count
return average
}
try averageSpeed(records: records)
/* RESULT: 108 */
$0 as the resulting value
.onChanged and .onEnded modifiers perform an #escaping closure when a SwiftUI gesture changed or ended.
#State private var rotate: Angle = .zero
var myGesture: some Gesture {
RotationGesture()
.onChanged { rotate = $0 }
.onEnded { angle in rotate = angle }
}
var body: some View {
Rectangle()
.rotationEffect(rotate)
.gesture(myGesture)
}
Closure as array's element
var closureArray: [() -> Void] = []
var counter: Int = 0
for _ in 1...2 {
closureArray.append {
counter += 1
print(counter)
}
}
closureArray.forEach { $0() }
closureArray.forEach { closure in closure() }
/* RESULT: */
/* 1 */
/* 2 */
/* 3 */
/* 4 */
Swift vs Kotlin vs Python
Also, let's see how Kotlin's lambda is similar to Swift's closure:
Swift
let element: [String] = ["Argentum","Aurum","Platinum"]
let characterCount = element.map { $0.count }
print(characterCount)
/* RESULT: [8, 5, 8] */
Kotlin
Kotlin's lambda expression has just one parameter with implicit name: it. In other words, if you have a function literal with exactly one parameter you don’t need to define that parameter explicitly, you can use it instead (like $0 in Swift).
val element = listOf("Argentum","Aurum","Platinum")
val characterCount = element.map { it.length }
println(characterCount)
/* RESULT: [8, 5, 8] */
But in Python there's no equivalent of Shorthand Argument Name.
Python
element = ["Argentum","Aurum","Platinum"]
characterCount = list(map(lambda x: len(x), element))
print(characterCount)
# RESULT: [8, 5, 8]
It represents shorthanded arguments sent into a closure, this example breaks it down:
Swift 4:
var add = { (arg1: Int, arg2: Int) -> Int in
return arg1 + arg2
}
add = { (arg1, arg2) -> Int in
return arg1 + arg2
}
add = { arg1, arg2 in
arg1 + arg2
}
add = {
$0 + $1
}
let result = add(20, 20) // 40
The refer to the first and second arguments of sort. Here, sort compares 2 elements and order them.
You can look up Swift official documentation for more info:
Swift automatically provides shorthand argument names to inline
closures, which can be used to refer to the values of the closure’s
arguments by the names $0, $1, $2, and so on.
In Addition with #Bobby's Answer I would like to Add an Example
var add: (Int,Int,Int)->Int
add = {
//So here the $0 is first argument $1 is second argument $2 is third argument
return $0 + $1 + $2
//The above statement can also be written as $0 + $1 + $2 i.e is return is optional
}
let result = add(20, 30, 40)
print(result) // Prints 90
It is shorthand argument names.
Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.
If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The in keyword can also be omitted, because the closure expression is made up entirely of its body:
reversed = names.sort( { $0 > $1 } )
Here, $0 and $1 refer to the closure’s first and second String arguments.
$0, $1, and so on represent items of a collection when performing actions on them.
For instance, to print a list of numbers, you can use the built-in forEach() function and $0 like this:
let numbers = [1, 2, 3, 4, 5]
numbers.forEach { print($0) }
Here the $0 represents each number in the array at a time.
🛑 $0’s and $1’s in Swift
Before explaining what $0’s and $1’s are, let’s see an example.
Say you have an array of numbers, and you want to print them. You can do it with a for loop:
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
print(number)
}
But, there is another alternative using forEach with a $0:
numbers.forEach { print($0) }
Let’s inspect the code to see what it does:
It takes the numbers array and calls forEach to run code for each number element in the array.
The forEach method accepts a closure that takes a number argument. In this case, the closure is { print($0) }. This closure is called for each number in the array one by one.
On each call, $0 represents a different number argument from the numbers array.
🛑 A More Detailed Explanation
Let’s create a closure and assign it to a variable called printer. This closure takes a number as its argument and prints it out:
var printer = { (n: Int) -> Void in
print(n)
}
// Test calling printer closure on a number 3 for example:
printer(3) // prints 3 - it works
// Now, use printer to print numbers (from the previous example):
numbers.forEach(printer) // prints 1, 2, 3, 4, 5
You can simplify this closure: In the end, you use this closure to print an array of integers. Thus, Swift can automatically infer the argument type (integer) without explicitly specifying it. Thus, printer can be simplified to:
printer = { n in
print(n)
}
// Test: printing numbers still works:
numbers.forEach(printer) // prints 1, 2, 3, 4, 5
Now, here is where the $0’s come in: In Swift, it is also possible to omit the parameter name(s) in closures. When doing this, you can refer to the omitted arguments with $0, $1, $2.
Let’s simplify the printer closure by omitting the number parameter:
printer = { print($0) }
// forEach accepts a closure:
numbers.forEach(printer) // prints 1, 2, 3, 4, 5
Finally, nothing forces you to assign the printer to a variable called printer. You can use the closure anonymously to achieve the same behavior:
numbers.forEach { print($0) } // prints 1, 2, 3, 4, 5
This is the line you saw earlier.
Create an array where all the numbers in the numbers array are raised to the second power using map method.
let numbers = [1, 2, 3, 4, 5]
let pow_nums = numbers.map { $0 * $0 }
pow_nums.forEach { print($0) } // Prints 1, 4, 9, 16, 25
Code Happy ;)