I'm trying to write a function literal in swift with a recursive body - in this case it's simply to add all the values in a list. I'm getting an error that "Variable used within it's own initial value". Any thoughts on what might be wrong here? Also I'm aware that what I'm doing here is a simple reduce and that it's build into Array, I'm just using this as an illustrative example of what I'm seeing elsewhere.
let list: Slice = [1,2,3,4,5,6,7,8,9,10]
var closure = { (memo: Int, list: Slice<Int>) -> Int in
if (list.count == 0) {
return memo
} else {
return closure(memo + list[0], list[1..<list.count])
}
}
let value = closure(0,list)
Try this:
let list: Slice = [1,2,3,4,5,6,7,8,9,10]
var closure:((Int, Slice<Int>) -> Int)!
closure = { (memo, list) in
if (list.count == 0) {
closure = nil // remove retain cycle
return memo
} else {
return closure(memo + list[0], list[1..<list.count])
}
}
let value = closure(0, list)
EDIT:
see this video: Advanced Swift at WWDC14. from around 41:00. it shows the down side of this method, and better workaround.
I know this is quite old, but I've found another alternative:
let list : ArraySlice<Int> = [1,2,3,4,5,6,7,8,9,10]
let closure = { (Void) -> ((Int, ArraySlice<Int>) -> Int) in
func f(memo: Int, list: ArraySlice<Int>) -> Int {
if (list.count == 0) {
return memo
} else {
return f(memo + list[list.startIndex], list: list[(list.startIndex + 1)..<list.endIndex])
}
}
return f
}()
let value = closure(0, list)
Related
I don't understand why I am getting the following error when executing this function:
error: unexpected non-void return value in void function
return [index, dic[value]!]
func findIndexes(_ nums: [Int], _ target: Int) -> [Int] {
var dic = [Int:Int]()
var answer: [Int] = []
nums.enumerated().forEach { index, value in
//the goal is to get the index of the value matching the difference
//check if current value == anything in dictionary
//MARK: Add the Index and Difference to Dictionary
let difference = (target - value)
if (dic.keys.contains(value) && !dic.values.contains(index)) {
return [index, dic[value]!]
}
dic[difference] = index
}
return []
}
print(findIndexes([0,11,15,2,7], 9))
I'm unclear what you're trying to do, but this is a version of what you are doing that compiles:
func findIndexes(_ nums: [Int], _ target: Int) -> [Int] {
var dic = [Int:Int]()
var answer: [Int] = []
for (index, value) in nums.enumerated() {
let difference = (target - value)
if (dic.keys.contains(value) && !dic.values.contains(index)) {
return [index, dic[value]!]
}
dic[difference] = index
}
return []
}
Why the difference? for...in is a loop. You can return from the surrounding function from within it, break out of it, etc. forEach loops implicitly but it is not a loop; it takes a closure, and you can't return out of that unless it's a closure that yields a return value, and this one doesn't; its type, as you can see from the docs, is (Self.Element) throws -> Void — and Void means "no return value".
If you insist on using forEach, you can do what you're trying to do with a bit of extra jiggery-pokery:
func findIndexes(_ nums: [Int], _ target: Int) -> [Int] {
var dic = [Int:Int]()
var answer: [Int] = []
nums.enumerated().forEach { index, value in
let difference = (target - value)
if (dic.keys.contains(value) && !dic.values.contains(index)) {
answer = [index, dic[value]!]
return
}
dic[difference] = index
}
return answer
}
I think that does what you're trying to do, but I'm not sure; it seems unnecessarily obscure. In my personal opinion, the way to program is to Say What You Mean (SWYM).
I wanted to write a Swift version of the classic cache data structure that uses a array up until a certain configurable limit, then switches to a map[2].
Unfortunately the performance characteristics I get when writing even simpler data structure makes me think that this is not doable in swift at the moment.
Consider this piece of code :
import Foundation
let aKey = 196
var cache: [(Int, String)] = []
for _ in 0..<1_000_000_000
{
var t: String?
for (k, v) in cache
{
if k == aKey
{
t = v
break
}
}
if t == nil
{
let str = "some cache value with key=\(aKey)"
cache.append((aKey, str))
t = str
}
}
This takes around 0.785s to execute.
This other program, which should be roughly equivalent :
import Foundation
struct DummyCache<K:Hashable, V>
{
var array:[(K, V?)] = []
subscript(index: K) -> V?
{
get
{
for (k, v) in array
{
if k == index
{
return v
}
}
return nil
}
set(newValue)
{
array.append((index, newValue))
}
}
}
let aKey = 196
var cache: DummyCache<Int, String> = DummyCache()
cache[aKey] = "some cache value with key=\(aKey)"
for _ in 0..<1_000_000_000
{
var t: String? = cache[aKey]
}
Is in fact 45x slower! (around 35.532s)
When profiling this code, I can see that most of the time is spent in the swift runtime :
Is there a way to improve the performance of this ?
[2] For the record, here is the data structure I had in mind :
import Foundation
struct Cache<K:Hashable, V>
{
let limit: Int
var array:[(K, V)]? = nil
var map: [K:V]? = nil
let buildValueFromKey: (K) -> V
init(limit: Int = 20, buildValueFromKey:#escaping (K) -> V) //optimal limit depends, must conduct tests to find optimal value
{
self.limit = limit
self.buildValueFromKey = buildValueFromKey
}
subscript(index: K) -> V
{
mutating get
{
if var map = map
{
if let res = map[index]
{
return res
}
else
{
let res = buildValueFromKey(index)
map[index] = res
return res
}
}
else //array mode
{
if array == nil
{
let res = buildValueFromKey(index)
array = [(index, res)]
return res
}
//else
for (k, v) in array! //can't be null at that point
{
if k == index
{
return v
}
}
// array does not contain index at this point, create & append it
let res = buildValueFromKey(index)
array!.append((index, res))
return res
}
}
}
}
let aKey = 196
var cache: Cache<Int, String> = Cache{ "some cache value with key=\($0)" }
for _ in 0..<1_000_000_000
{
var t: String? = cache[aKey]
}
So good question and still no answer. I haven't profiled your code, but have some possible solution. In case you experience performance drops with array of objects (not of structs/POTs), take a looks at ContiguousArray - it was invented especially to eliminate retains/releases moving to block-based allocations.
Here is official docs: https://developer.apple.com/documentation/swift/contiguousarray
And more tips for inspiration: https://github.com/apple/swift/blob/main/docs/OptimizationTips.rst
Xcode 8.3.3 is giving me this Swift 3 error on this line
values2[index] = nextValue(currentValue)
Cannot convert value of type 'Int' to expected argument type 'Card'
Here's my code:
//
// Card.swift
// match
//
// Created by quantum on 05/09/2017.
// Copyright © 2017 Quantum Productions. All rights reserved.
//
import UIKit
class Card: NSObject {
var quantity = 0
var fill = 0
var shape = 0
var color = 0
override var description : String {
return "Q" + String(quantity) + "/F" + String(fill) + "/S" + String(shape) + "/C" + String(color)
}
override init() {
super.init()
}
static func properties() -> [String] {
return ["quantity", "fill", "shape", "color"]
}
static func isMatch(cards: [Card]) -> Bool {
for property in self.properties() {
var sum = 0
for card in cards {
sum = sum + (card.value(forKey: property) as! Int)
}
if !([3, 6, 9, 7].contains(sum)) {
return false
}
}
return true
}
static func deck(_ values: [Int], _ index: Int, _ max: Int, _ acc: [Card]) -> [Card]{
let currentValue = values[index]
var values2 = values
if currentValue >= max {
if index == 0 {
return acc
}
values2[index] = 0
values2[index-1] = values2[index-1] + 1
return deck(values, index - 1, max, acc)
} else {
var acc2 = acc
let card = Card()
for (index, element) in self.properties().enumerated() {
card.setValue(values[index], forKey: element)
}
acc2.append(Card())
values2[index] = nextValue(Card())
return deck(values2, index, max, acc2)
}
}
func nextValue(_ v: Int) -> Int {
if (v == 0) {
return 1
} else if (v == 1) {
return 2
}
return 4
}
static func deck() -> [Card] {
return deck([1,1,1,1], 4, 3, [Card]())
}
}
this is inside of my Card class.
Strangely, if I try (this is wrong, I'm testing the compiler error)
values2[index] = nextValue(Card())
I get the error Cannot assign the value of type (Int) -> Int to type 'Int'.
Swift thinks my Card is an Int? I'm confused as to what's happening.
I expected to get the call nextvalue with the variable currentvalue, which should be an Int.
It's a bad error message from the compiler.
Your problem is that deck is declared static, but you're trying to call nextValue which is not declared static. This means that nextValue implicitly takes a hidden argument, self, but deck isn't providing it.
If you add static to the func nextValue declaration, it will work like you expect. (You'll get an error on the line referring to self.properties instead, but you'll be closer.)
To make this work properly, you probably want all these functions to be non-static instead. Just think about how this code gets called initially (i.e. how you get your first instance of Card).
A static method cannot call an instance method: the idea makes no sense, as there is no instance. Thus your reference to nextValue is impossible. That is why the line is problematic. How can a static method deck call an instance method nextValue?
ruby has the function string.squeeze, but I can't seem to find a swift equivalent.
For example I want to turn bookkeeper -> bokepr
Is my only option to create a set of the characters and then pull the characters from the set back to a string?
Is there a better way to do this?
Edit/update: Swift 4.2 or later
You can use a set to filter your duplicated characters:
let str = "bookkeeper"
var set = Set<Character>()
let squeezed = str.filter{ set.insert($0).inserted }
print(squeezed) // "bokepr"
Or as an extension on RangeReplaceableCollection which will also extend String and Substrings as well:
extension RangeReplaceableCollection where Element: Hashable {
var squeezed: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
let str = "bookkeeper"
print(str.squeezed) // "bokepr"
print(str[...].squeezed) // "bokepr"
I would use this piece of code from another answer of mine, which removes all duplicates of a sequence (keeping only the first occurrence of each), while maintaining order.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var alreadyAdded = Set<Iterator.Element>()
return self.filter { alreadyAdded.insert($0).inserted }
}
}
I would then wrap it with some logic which turns a String into a sequence (by getting its characters), unqiue's it, and then restores that result back into a string:
extension String {
func uniqueCharacters() -> String {
return String(self.characters.unique())
}
}
print("bookkeeper".uniqueCharacters()) // => "bokepr"
Here is a solution I found online, however I don't think it is optimal.
func removeDuplicateLetters(_ s: String) -> String {
if s.characters.count == 0 {
return ""
}
let aNum = Int("a".unicodeScalars.filter{$0.isASCII}.map{$0.value}.first!)
let characters = Array(s.lowercased().characters)
var counts = [Int](repeatElement(0, count: 26))
var visited = [Bool](repeatElement(false, count: 26))
var stack = [Character]()
var i = 0
for character in characters {
if let num = asciiValueOfCharacter(character) {
counts[num - aNum] += 1
}
}
for character in characters {
if let num = asciiValueOfCharacter(character) {
i = num - aNum
counts[i] -= 1
if visited[i] {
continue
}
while !stack.isEmpty, let peekNum = asciiValueOfCharacter(stack.last!), num < peekNum && counts[peekNum - aNum] != 0 {
visited[peekNum - aNum] = false
stack.removeLast()
}
stack.append(character)
visited[i] = true
}
}
return String(stack)
}
func asciiValueOfCharacter(_ character: Character) -> Int? {
let value = String(character).unicodeScalars.filter{$0.isASCII}.first?.value ?? 0
return Int(value)
}
Here is one way to do this using reduce(),
let newChar = str.characters.reduce("") { partial, char in
guard let _ = partial.range(of: String(char)) else {
return partial.appending(String(char))
}
return partial
}
As suggested by Leo, here is a bit shorter version of the same approach,
let newChar = str.characters.reduce("") { $0.range(of: String($1)) == nil ? $0.appending(String($1)) : $0 }
Just Another solution
let str = "Bookeeper"
let newChar = str.reduce("" , {
if $0.contains($1) {
return "\($0)"
} else {
return "\($0)\($1)"
}
})
print(str.replacingOccurrences(of: " ", with: ""))
Use filter and contains to remove duplicate values
let str = "bookkeeper"
let result = str.filter{!result.contains($0)}
print(result) //bokepr
What I'm trying to accomplish in imperative:
var mapNames = [String]()
var mapLocation = [String]()
for valueMap in valueMaps {
if let name = valueMap.name {
mapNames.append(name)
}
if let location = valueMap.location {
mapLocation.append(location)
}
}
What's the best way using a high order function or perhaps an array method (array.filter etc.) to compact the code above and also avoid using the for loop
Here is what I have tried, but the compiler gives an error:
let getArrayOfNames = valueMaps.filter() {
if let name = ($0 as valueMaps).name as [String]! {
return name;
}
}
let getArrayOfLocations = valueMaps.filter() {
if let type = ($0 as valueMaps).location as [String]! {
return type;
}
}
You need both filter() and map() :
let mapNames = valueMaps.filter( {$0.name != nil }).map( { $0.name! })
let mapLocations = valueMaps.filter( {$0.location != nil }).map( { $0.location! })
The filter takes a predicate as an argument (which specifies which
elements should be included in the result), and the map takes
a transformation as an argument. You were trying to merge both
aspects into the filter, which is not possible.
Update: As of Swift 2(?) has a flatMap() method for sequences, which
can be used to obtain the result in a single step:
let mapNames = valueMaps.flatMap { $0.name }
The closure is applied to all array elements, and the return value is an
array with all non-nil unwrapped results.
The filter() function needs its closure to return a bool - not the value you want to store in an array. You could chain filter and map together to get what you want, then:
let getArrayOfNames = valueMaps
.filter { $0.name != nil }
.map{ $0.name! }
Or, to do it in one function, with reduce:
let getArrayOfNames = valueMaps
.reduce([String]()) {
accu, element in
if let name = element.name {
return accu + [name]
} else {
return accu
}
}
Actually, the reduce can be a little better:
let getArrayOfNames = valueMaps.reduce([String]()) {
(names, value) in names + (value.name.map{[$0]} ?? [])
}
let getArrayOfLocations = valueMaps.reduce([String]()) {
(locs, value) in locs + (value.location.map{[$0]} ?? [])
}