> On Aug 17, 2017, at 10:05 AM, Erica Sadun via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Also, for those of you here who haven't heard my previous rant on the 
> subject, I dislike using map for generating values that don't depend on 
> transforming a domain to a range. (It has been argued that `_ in` is mapping 
> from `Void`, but I still dislike it immensely)

Can you please elaborate why (or maybe point me at the rant)? Does the same 
argument apply to `for _ in 1..<N { … }`?

But back to the discussion. I quickly looked at the sil being generated, and 
measured the performance of:

public class C {
  init() {}
}

@inline(never)
func foo(n: Int) -> [C] {
  var result: [C] = []
  result.reserveCapacity(n)
  for _ in 1...n {
    result.append(C())
  }
  return result
}

@inline(never)
func bar(n: Int) -> [C] {
  return (1...n).map { _ in C() }
}

Version using map wins in both the size of sil and performance. It is also 
easier to implement right (fewer moving parts, I guess). If one forgets to call 
`reserveCapacity` on `result` in the first case, performance drops even further.

Max

> 
> Here are the ways that I have approached this:
> 
> // Ugh
> [UIView(), UIView(), UIView(), UIView(), UIView()]
> 
> // No:
> let viewsA = Array(repeating: UIView(), count: 5) 
> // You end up with 5 of the same view
> 
> // Two statements that really should be one
> var views: [UIView] = []
> for _ in 1 ... 5 { views.append(UIView()) }
> 
> // Wrong use of `map`,  because it's mapping over `Void`
> let viewsA = (1 ... 5).map({ _ in UIView() }) 
> 
> // You can introduce `collect` specifically for the `_ in` in case, matching 
> other languages:
> public func collect<T>(_ generator: () throws -> T) rethrows -> [T]
> 
> // I think these are just ugly
> let viewsA__ = sequence(first: UIView(), next: { _ in UIView() 
> }).lazy.prefix(5)
> let viewsB__ = sequence(state: 5, next: { defer { $0 -= 1 }; $0 == 0 ? nil : 
> UIView() }
> 
> // You can build an iterator
> 
> let labeler = AnyIterator({ return UILabel() })
> let labels4 = Array(labeler.prefix(5))
> 
> // which lets you create multiple "slices" off the iterator
> 
> let randoms = AnyIterator({ return Int(arc4random_uniform(100) )})
> 
> print(Array(randoms.prefix(5)))
> print(Array(randoms.prefix(5)))
> 
> 
> // A little complex and I don't like making this `Int` based, because it 
> pulls the semantics away from sequences/collections
> 
> extension Int {
>     func of<T>(_ generator: @autoclosure () -> T) -> [T] {
>         assert(self >= 0, "cannot generate negative-count collection")
>         return (0 ..< self).map({ _ in generator() })
>     }
> }
> 
> 5.of(UIView())
> 
> // Even worse
> protocol Constructable {
>     init()
> }
> 
> extension UIView: Constructable {}
> 
> extension Int {
>     func of<T: Constructable>(_ generator: @autoclosure () -> T) -> 
> UnfoldSequence<T, Int> {
>         assert(self > 0, "cannot generate negative-count collection")
>         return sequence(state: self, next: { (state: inout Int) -> T? in
>             defer { state -= 1 }; return state == 0 ? nil : T.init() })
>     }
> }
> 
> print(Array(5.of(UIView())))
> 
> // or
> 
> extension Int {
>     func of<T>(_ generator: @escaping @autoclosure () -> T) -> 
> LazyMapRandomAccessCollection<(CountableRange<Int>), T> {
>         assert(self > 0, "cannot generate negative-count collection")
>         return (0 ..< self).lazy.map({ _ in generator() })
>     }
> }
> 
> // where some people preferred calling this `elements`, for example 
> `5.elements(of: UIView())`
> 
> // You can go Array:
> 
> extension Array {
>     /// Creates a new array containing the specified number of values created 
> by repeating a generating closure.
>     ///
>     /// - Parameters:
>     ///   - count: The number of times to apply the closure. `count` must be 
> zero or greater.
>     ///   - generator: The closure to execute
>     public init(count: Int, repeating: () -> Element) {
>         precondition(count >= 0, "")
>         self.init((1 ... count).map({ _ in repeating() }))
>     }
> }
> 
> let labels = Array(count: 4) { UILabel() }
> 
> // Or similarly
> 
> extension Array {
>     init(repeat count: Int, byCalling generator: @autoclosure () -> Element) {
>         self = (1 ... count).map({ _ in generator() })
>     }
> }
> 
> // From Oliver H:
> 
> extension Array {
>   convenience init(count: Int, repeating: () -> Elements) {
>     self = Array( (0..<count).map { _ in repeating() } )
>   }
> }
> 
> let views: [UIView] = Array(count: 5) { UIView(frame: .zero) }
> 
> // This one is from Soroush K. I think it's clever but I wouldn't really use 
> it
> 
> func * <T>(generator: @autoclosure () -> T, n: Int) -> [T] {
>   (0..<n).map({ _ in generator() }
> }
> 
> UIView() * 5
> 
> 
>> On Aug 17, 2017, at 10:36 AM, Tony Allevato via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> 
>> 
>> On Thu, Aug 17, 2017 at 9:20 AM Ross O'Brien <narrativium+sw...@gmail.com 
>> <mailto:narrativium%2bsw...@gmail.com>> wrote:
>> (0..<3).map{ _ in UIView() } - map already returns an Array.
>> 
>> Array((0..<3).map{ _ in UIView() }) is redundant.
>> 
>> Ah, right, thanks for pointing that out. I couldn't remember off the top of 
>> my head whether it returned an array or some kind of special sequence type 
>> that would need to be converted over.
>> 
>> In that case, I still think the map version wins—it's very clear that a 
>> repeated *operation* is occurring, whereas the originally proposed 
>> @autoclosure version hides that very important semantic information.
>> 
>> 
>> I've fallen foul before, of trying to create an array of six buttons and 
>> getting an array of one button six times; I think that should be easier. But 
>> if each button corresponds to an existing value or needs to be initialised 
>> based on its index in that array, map transposing values or indices into 
>> buttons is already covered.
>> 
>> On Thu, Aug 17, 2017 at 5:03 PM, Tony Allevato via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> Couldn't this be rewritten more simply today as:
>> 
>>     Array((0..<3).map { index in MyView(forIndex: index) })
>> 
>> And the version that doesn't need the index could be written:
>> 
>>     Array((0..<3).map { _ in UIView() })
>> 
>> The AnyIterator approach posted above is also nice—I wouldn't have thought 
>> of that one. But I suppose that only works in the case where you don't need 
>> the index.
>> 
>> So the question is, should we increase the API surface of Array for 
>> something that (IMO) is already fairly straightforward to do? The nice thing 
>> about the approaches above is that they're composable. Array doesn't have to 
>> make any assumptions about closures or index arguments to those closures; it 
>> just takes a sequence and we already have primitives to construct sequences 
>> of new objects using .map. Array(repeating:count:) and 
>> repeatedElement(_:count:) are nice when the value being repeated is fixed, 
>> but I think once you start getting into questions like "what if I need a 
>> different thing each time?" or "what if I need to involve the index as part 
>> of the elements?" then it's better suited to compose the features already 
>> there to build something up than to add new APIs that try to cover each of 
>> these special cases.
>> 
>> 
>> On Thu, Aug 17, 2017 at 8:40 AM Christopher Kornher via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> We might as well add the index to the call so elements can be created from 
>> other lists, etc.
>> 
>>  Array(repeatedlyCalling: { (index:Int) in MyView( forIndex:index ) }, 
>> count: 3) // This might by syntactically correct...
>> 
>> 
>> 
>>> On Aug 17, 2017, at 9:24 AM, Rob Mayoff via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> On Thu, Aug 17, 2017 at 7:04 AM, Robert Bennett via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> Alternatively, instead of replacing the current definition with an 
>>> autoclosure version, we could leave the current version in place and add a 
>>> version taking a function. This could be especially useful when you’ve 
>>> defined a function like `makeMySpecialKindOfButton() -> UIButton` that does 
>>> a lot of customization to an object before returning it.
>>> 
>>> Array(repeating: { return UIView() }, count: 3)
>>> and
>>> Array(repeating: makeMySpecialKindOfButton, count: 3)
>>> 
>>> This is still source-breaking, because an array of closures is legal. We 
>>> need a different name to avoid breaking source:
>>> 
>>>     Array(repeatedlyCalling: { UIView() }, count: 3)
>>>  
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to