profile
viewpoint
Nate Cook natecook1000 @apple Evanston, IL

natecook1000/Collections-in-Swift 5

Code samples from my CocoaConf talk

airspeedswift/swift 2

The Swift Programming Language

natecook1000/Alamofire 1

Elegant HTTP Networking in Swift

natecook1000/BlocksKit 1

The Objective-C block utilities you always wish you had.

natecook1000/ABCalendarPicker 0

Fully configurable iOS calendar UI component with multiple layouts and smooth animations.

natecook1000/Aerial 0

Apple TV Aerial Screensaver for Mac

natecook1000/AFIncrementalStore 0

Core Data Persistence with AFNetworking, Done Right

natecook1000/AFNetworking 0

A delightful iOS and OS X networking framework

natecook1000/AlamofireImage 0

AlamofireImage is an image component library for Alamofire

natecook1000/Antenna 0

Extensible Remote Logging for iOS

push eventapple/swift-argument-parser

CypherPoet

commit sha bb370a320b229aa8f9eb0352d4fbc6e9874325f8

Fix small typo in `CommandConfiguration` documentation (#252)

view details

push time in a day

PR merged apple/swift-argument-parser

Fix small typo in `CommandConfiguration` documentation

Checklist

  • [ ] I've added at least one test that validates that my change is working, if appropriate
  • [x] I've followed the code style of the rest of the project
  • [x] I've read the Contribution Guidelines
  • [x] I've updated the documentation if necessary
+1 -1

4 comments

1 changed file

CypherPoet

pr closed time in a day

Pull request review commentapple/swift-argument-parser

[Windows] Don't try to detect shell.

 public struct CompletionShell: RawRepresentable, Hashable, CaseIterable {    /// Returns an instance representing the current shell, if recognized.   public static func autodetected() -> CompletionShell? {+#if os(Windows)+    return nil+#elseif

Looks like this should just be an else:

#else
damuellen

comment created time in a day

PullRequestReviewEvent

pull request commentapple/swift-argument-parser

Fix small typo in `CommandConfiguration` documentation

@swift-ci Please test

CypherPoet

comment created time in a day

pull request commentapple/swift-argument-parser

Fix small typo in `CommandConfiguration` documentation

A+++ 🎉

CypherPoet

comment created time in a day

pull request commentapple/swift-argument-parser

[Windows] Don't try to detect shell.

@swift-ci Please test

damuellen

comment created time in a day

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// Returns a collection stepping through the elements every `step` starting+  /// at the first value. Any remainders of the stride will be trimmed.+  ///+  ///     (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+  ///     (0...10).striding(by: 3) // == [0, 3, 6, 9]+  ///+  /// - Complexity: O(1). Access to successive values is O(1) if the+  /// collection conforms to `RandomAccessCollection`; otherwise,+  /// O(_k_), where _k_ is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride {+  public func striding(by step: Int) -> Self {+    Stride(base: base, stride: stride * step)+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }++  public var count: Int {+    let limit = base.count - 1+    return limit / stride + (limit < 0 ? 0 : 1)+  }+  +  public func distance(from start: Index, to end: Index) -> Int {+    let distance = base.distance(from: start.base, to: end.base)+    return distance / stride + (distance % stride > 0 ? 1 : 0)+  }+  +  public func index(_ i: Index, offsetBy distance: Int) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")

This isn't a valid precondition, since c.index(c.endIndex, offsetBy: -1) is valid for bidirectional collections.

ollieatkinson

comment created time in 2 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// Returns a collection stepping through the elements every `step` starting+  /// at the first value. Any remainders of the stride will be trimmed.+  ///+  ///     (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+  ///     (0...10).striding(by: 3) // == [0, 3, 6, 9]+  ///+  /// - Complexity: O(1). Access to successive values is O(1) if the+  /// collection conforms to `RandomAccessCollection`; otherwise,+  /// O(_k_), where _k_ is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride {+  public func striding(by step: Int) -> Self {+    Stride(base: base, stride: stride * step)+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }++  public var count: Int {+    let limit = base.count - 1+    return limit / stride + (limit < 0 ? 0 : 1)+  }+  +  public func distance(from start: Index, to end: Index) -> Int {+    let distance = base.distance(from: start.base, to: end.base)+    return distance / stride + (distance % stride > 0 ? 1 : 0)+  }+  +  public func index(_ i: Index, offsetBy distance: Int) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    let limit = distance > 0 ? endIndex : startIndex+    return index(i, offsetBy: distance, limitedBy: limit) ?? limit

Instead, you can add a precondition that the result of this call is non-nil.

ollieatkinson

comment created time in 2 days

PullRequestReviewEvent
PullRequestReviewEvent

push eventapple/swift-algorithms

Roshan Kumar Sah

commit sha 66ec96812a70918b5dbdeb1a1d98390037b64642

Added documentation and where clause for API `uniqued()` (#32)

view details

push time in 3 days

PR merged apple/swift-algorithms

Added documentation and where clause for API `uniqued()`

<!-- Problem: A quick look for the API names unique() -->

Problem: * A quick look up for the API named uniqued() with example associated with it was missing. * API named uniqued() while enumerating each element was checking an if condition.

Solution: * API documentation added to address the same. * where clause added for the same

Checklist

  • [ yes] I've added at least one test that validates that my change is working, if appropriate
  • [ yes] I've followed the code style of the rest of the project
  • [ yes] I've read the Contribution Guidelines
  • [ yes] I've updated the documentation if necessary
+29 -2

3 comments

1 changed file

Roshankumar350

pr closed time in 3 days

pull request commentapple/swift-algorithms

Added documentation and where clause for API `uniqued()`

Looks great, @Roshankumar350 — thanks for sticking with this! 🎉

Roshankumar350

comment created time in 3 days

pull request commentapple/swift-argument-parser

[Windows] Don't try to detect shell.

@swift-ci Please test

damuellen

comment created time in 4 days

pull request commentapple/swift-argument-parser

[Windows] Don't try to detect shell.

@swift-ci Please smoke test

damuellen

comment created time in 4 days

pull request commentapple/swift-argument-parser

[Windows] Don't try to detect shell.

Thanks for this, @damuellen!

damuellen

comment created time in 4 days

pull request commentapple/swift-argument-parser

[Windows] use _dupenv_s instead of getenv

The windows environment doesn't have a SHELL environment variable, your shell is either cmd or powershell.

🤔 In that case, given that bash, fish, and zsh are the only three choices here, should CompletionShell.autodetected() just return nil here?

damuellen

comment created time in 5 days

pull request commentapple/swift-argument-parser

[Windows] use _dupenv_s instead of getenv

cc @compnerd

damuellen

comment created time in 5 days

pull request commentapple/swift-argument-parser

Support Exit codes from thrown CustomNSError conformers

Thanks for this contribution, @SergeyPetrachkov! I think we can test this without adding the separately compiled example — can you add your tests for handling CustomNSError types to ExitCodeTests.swift?

SergeyPetrachkov

comment created time in 5 days

pull request commentapple/swift-argument-parser

Fix small typo in `CommandConfiguration` documentation

@CypherPoet Thanks for this fix! Would you mind clearing out the whitespace-only changes?

CypherPoet

comment created time in 5 days

issue commentapple/swift-argument-parser

Allow subcommands to have alias names

@Zoha131 Thanks for your interest in working on this!

  1. Would the aliasing be an opt-in feature?

Yes — I think the aliases could just be an array of strings, with the default as an empty array.

  1. Would this only be available for that subcommand whose commandName is not nil?

Right — a nil value for commandName means the primary name should be generated automatically from the type name.

You might want to think about how to present the aliases to users in the help, as well.

@MPLew-is / @ajh17: I'm disinclined to add this kind of aliasing, as it somewhat inverts the command tree metaphor. I think we have enough opportunities with composition that this kind of forwarding can be handled by existing language features. For example, you could create a generic wrapper command that includes another command as an @OptionGroup and then runs it automatically:

struct ForwardingCommand<Base: ParsableCommand>: ParsableCommand {
  static var configuration: CommandConfiguration {
    Base.configuration // might need a little extra something here
  }
  
  @OptionGroup
  var command: Base
  
  mutating func run() throws {
    try command.run()
  }
}
mlaster

comment created time in 5 days

Pull request review commentapple/swift-algorithms

Added documentation and where clause for API `uniqued()`

 extension Sequence where Element: Hashable {  extension Sequence {   /// Returns an array with the unique elements of this sequence (as determined-  /// by the given projection), in the order of the first occurence of each+  /// by the given projection), in the order of the first occurrence of each   /// unique element.+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "cow", "owl"]+  ///     let uniqued = animals.uniqued(on: {$0.first})+  ///     print(uniqued)+  ///     // Prints '["dog", "pig", "cat", "ox"]'+  ///+  /// - Parameter projection: A projecting closure. `projection` accepts an+  ///   element of this sequence as its parameter which is having the type of+  ///   projecting element and returns a projected value that may have the same+  ///   type having constraint `Hashable`.

How about this?

  /// - Parameter projection: A closure that transforms an element into the
  ///   value to use for uniqueness. If `projection` returns the same value
  ///   for two different elements, the second element will be excluded
  ///   from the resulting array.
Roshankumar350

comment created time in 5 days

Pull request review commentapple/swift-algorithms

Added documentation and where clause for API `uniqued()`

 extension Sequence where Element: Hashable {  extension Sequence {   /// Returns an array with the unique elements of this sequence (as determined-  /// by the given projection), in the order of the first occurence of each+  /// by the given projection), in the order of the first occurrence of each   /// unique element.+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "cow", "owl"]+  ///     let uniqued = animals.uniqued(on: {$0.first})

Let's give an introduction to this example, as well:

  /// This example finds the elements of the `animals` array with unique
  /// first characters:
  ///
  ///     let animals = ["dog", "pig", "cat", "ox", "cow", "owl"]
  ///     let uniqued = animals.uniqued(on: { $0.first })
Roshankumar350

comment created time in 5 days

PullRequestReviewEvent
PullRequestReviewEvent

created tagapple/swift-algorithms

tag0.0.2

Swift Algorithms is an open-source package of sequence and collection algorithms, along with their related types.

created time in 6 days

release apple/swift-algorithms

0.0.2

released time in 6 days

push eventapple/swift-algorithms

Nate Cook

commit sha bb3bafeca0e164ece3403a9de646b7d38c07dd49

Update changelog for 0.0.2 release

view details

push time in 6 days

push eventapple/swift-algorithms

Nate Cook

commit sha c582aa9d22daa4ea1a053bbb3fedf3edf5d88b0f

Rename `chained(with:)` to `chain(_:_:)` (#33) * Rename `chained(with:)` to `chain(_:_:)` This follows a forum discussion about the `chained(with:)` name. The old names are preserved as deprecated symbols. * Remove conditional lazy conformance from `Chain2`

view details

push time in 6 days

delete branch apple/swift-algorithms

delete branch : nate/chained_to_chain

delete time in 6 days

PR merged apple/swift-algorithms

Rename `chained(with:)` to `chain(_:_:)`

Description

This follows a forum discussion about the chained(with:) name. The old names are preserved as deprecated symbols.

Detailed Design

The new version of chain looks like this:

func chain<S1, S2>(_ s1: S1, _ s2: S2) -> Chain2<S1, S2>

Documentation Plan

Updated the symbol documentation, guide, and README.

Test Plan

Updated existing tests to use the new name.

Source Impact

This deprecates the chained(with:) method and the Chain2 type.

Checklist

  • [x] I've added at least one test that validates that my change is working, if appropriate
  • [x] I've followed the code style of the rest of the project
  • [x] I've read the Contribution Guidelines
  • [x] I've updated the documentation if necessary
+100 -88

0 comment

4 changed files

natecook1000

pr closed time in 6 days

push eventapple/swift-algorithms

Nate Cook

commit sha 4c89606c0813da9e5d44df36e3fecac5f4252d79

Remove conditional lazy conformance from `Chain2`

view details

push time in 6 days

Pull request review commentapple/swift-algorithms

Rename `chained(with:)` to `chain(_:_:)`

 extension Chain: BidirectionalCollection   } } -extension Chain: RandomAccessCollection+extension Chain2: RandomAccessCollection   where Base1: RandomAccessCollection, Base2: RandomAccessCollection {}-extension Chain: LazySequenceProtocol where Base1: LazySequenceProtocol {}+extension Chain2: LazySequenceProtocol where Base1: LazySequenceProtocol {}

Yes! Another wrinkle in the various keep-it-lazy shenanigans.

natecook1000

comment created time in 6 days

PullRequestReviewEvent

pull request commentapple/swift

[Docs] Clarifies `ObjectIdentifier` guarantees

@swift-ci Please smoke test

sunbohong

comment created time in 6 days

pull request commentapple/swift

[Docs] Clarifies `ObjectIdentifier` guarantees

Thanks for identifying this issue and getting the docs updated, @sunbohong!

sunbohong

comment created time in 6 days

push eventsunbohong/swift

Nate Cook

commit sha a43cb01cbb572d187ab7eb1a8900d59c8eb04d5b

Update ObjectIdentifier.swift Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com>

view details

push time in 6 days

Pull request review commentapple/swift-algorithms

Add "sortedPrefix(_:by)" to Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//

Nit: I think you lost some ASCII art here!

rakaramos

comment created time in 8 days

Pull request review commentapple/swift-algorithms

Add "sortedPrefix(_:by)" to Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//++extension Collection {+  /// Returns the first k elements of this collection when it's sorted using+  /// the given predicate as the comparison between elements.+  ///+  /// This example partially sorts an array of integers to retrieve its three+  /// smallest values:+  ///+  ///     let numbers = [7,1,6,2,8,3,9]+  ///     let smallestThree = numbers.sortedPrefix(3, <)+  ///     // [1, 2, 3]+  ///+  /// If you need to sort a collection but only need access to a prefix of its+  /// elements, using this method can give you a performance boost over sorting+  /// the entire collection. The order of equal elements is guaranteed to be+  /// preserved.+  ///+  /// - Parameter count: The k number of elements to prefix.+  /// - Parameter areInIncreasingOrder: A predicate that returns true if its+  /// first argument should be ordered before its second argument;+  /// otherwise, false.+  ///+  /// - Complexity: O(k log k + nk)+  public func sortedPrefix(+    _ count: Int,+    by areInIncreasingOrder: (Element, Element) throws -> Bool+  ) rethrows -> [Self.Element] {+    assert(count >= 0, """+      Cannot prefix with a negative amount of elements!+      """+    )+    assert(count <= self.count, """+      Cannot prefix more than this Collection's size!+      """+    )

I don’t think we need this assertion — I’d expect this to act like prefix or randomSample and just return everything it can.

rakaramos

comment created time in 8 days

PullRequestReviewEvent
PullRequestReviewEvent

PR opened apple/swift-algorithms

Rename `chained(with:)` to `chain(_:_:)`

Description

This follows a forum discussion about the chained(with:) name. The old names are preserved as deprecated symbols.

Detailed Design

The new version of chain looks like this:

func chain<S1, S2>(_ s1: S1, _ s2: S2) -> Chain2<S1, S2>

Documentation Plan

Updated the symbol documentation, guide, and README.

Test Plan

Updated existing tests to use the new name.

Source Impact

This deprecates the chained(with:) method and the Chain2 type.

Checklist

  • [x] I've added at least one test that validates that my change is working, if appropriate
  • [x] I've followed the code style of the rest of the project
  • [x] I've read the Contribution Guidelines
  • [x] I've updated the documentation if necessary
+102 -84

0 comment

4 changed files

pr created time in 6 days

push eventapple/swift-algorithms

Nate Cook

commit sha 268452f9538c9907abe836d65024b904c99eebbc

Rename `chained(with:)` to `chain(_:_:)` This follows a forum discussion about the `chained(with:)` name. The old names are preserved as deprecated symbols.

view details

push time in 6 days

create barnchapple/swift-algorithms

branch : nate/chained_to_chain

created branch time in 6 days

PullRequestReviewEvent

Pull request review commentapple/swift-algorithms

Added documentation and where clause for API `uniqued()`

 //===----------------------------------------------------------------------===//  extension Sequence where Element: Hashable {-  /// Returns an array with only the unique elements of this sequence, in the-  /// order of the first occurence of each unique element.+  /// Returns an array with only  unique elements of this sequence, in the+  /// order of the first occurrence of each unique element.+  ///+  /// In this example, `uniqued()` is used to include only names which are+  /// unique+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "dog","cat"]+  ///     let uniqued = animals.uniqued()+  ///     print(uniqued)+  ///     // Prints "["dog", "pig","cat", "ox"]"`+  ///+  /// - Returns: Returns an array with only the unique elements of this sequence
  /// - Returns: An array with only the unique elements of this sequence.
Roshankumar350

comment created time in 6 days

Pull request review commentapple/swift-algorithms

Added documentation and where clause for API `uniqued()`

 //===----------------------------------------------------------------------===//  extension Sequence where Element: Hashable {-  /// Returns an array with only the unique elements of this sequence, in the-  /// order of the first occurence of each unique element.+  /// Returns an array with only  unique elements of this sequence, in the+  /// order of the first occurrence of each unique element.+  ///+  /// In this example, `uniqued()` is used to include only names which are+  /// unique+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "dog","cat"]+  ///     let uniqued = animals.uniqued()+  ///     print(uniqued)+  ///     // Prints "["dog", "pig","cat", "ox"]"`+  ///+  /// - Returns: Returns an array with only the unique elements of this sequence+  ///  .+  /// - Complexity: O(*n*), where *n* is the length of the sequence.   public func uniqued() -> [Element] {     var seen: Set<Element> = []     var result: [Element] = []-    for element in self {-      if seen.insert(element).inserted {+    for element in self where seen.insert(element).inserted {

We don't need this kind of lateral change, here and below.

Roshankumar350

comment created time in 6 days

Pull request review commentapple/swift-algorithms

Added documentation and where clause for API `uniqued()`

 //===----------------------------------------------------------------------===//  extension Sequence where Element: Hashable {-  /// Returns an array with only the unique elements of this sequence, in the-  /// order of the first occurence of each unique element.+  /// Returns an array with only  unique elements of this sequence, in the+  /// order of the first occurrence of each unique element.+  ///+  /// In this example, `uniqued()` is used to include only names which are+  /// unique+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "dog","cat"]+  ///     let uniqued = animals.uniqued()+  ///     print(uniqued)+  ///     // Prints "["dog", "pig","cat", "ox"]"`+  ///+  /// - Returns: Returns an array with only the unique elements of this sequence+  ///  .+  /// - Complexity: O(*n*), where *n* is the length of the sequence.   public func uniqued() -> [Element] {     var seen: Set<Element> = []     var result: [Element] = []-    for element in self {-      if seen.insert(element).inserted {+    for element in self where seen.insert(element).inserted {         result.append(element)-      }     }     return result-  }+    } }  extension Sequence {   /// Returns an array with the unique elements of this sequence (as determined-  /// by the given projection), in the order of the first occurence of each+  /// by the given projection), in the order of the first occurrence of each   /// unique element.+  ///+  /// In this example, `uniqued()` is used to include only names which are+  /// unique+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "dog","cat"]+  ///     let uniqued = animals.uniqued()+  ///     print(uniqued)+  ///     // Prints "["dog", "pig","cat", "ox"]"`

Could you switch this to an example that demonstrates the uniqued(on:) method? This example shows the parameterless method defined above.

Roshankumar350

comment created time in 6 days

Pull request review commentapple/swift-algorithms

Added documentation and where clause for API `uniqued()`

 //===----------------------------------------------------------------------===//  extension Sequence where Element: Hashable {-  /// Returns an array with only the unique elements of this sequence, in the-  /// order of the first occurence of each unique element.+  /// Returns an array with only  unique elements of this sequence, in the+  /// order of the first occurrence of each unique element.+  ///+  /// In this example, `uniqued()` is used to include only names which are+  /// unique+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "dog","cat"]+  ///     let uniqued = animals.uniqued()+  ///     print(uniqued)+  ///     // Prints "["dog", "pig","cat", "ox"]"`+  ///+  /// - Returns: Returns an array with only the unique elements of this sequence+  ///  .+  /// - Complexity: O(*n*), where *n* is the length of the sequence.   public func uniqued() -> [Element] {     var seen: Set<Element> = []     var result: [Element] = []-    for element in self {-      if seen.insert(element).inserted {+    for element in self where seen.insert(element).inserted {         result.append(element)-      }     }     return result-  }+    } }  extension Sequence {   /// Returns an array with the unique elements of this sequence (as determined-  /// by the given projection), in the order of the first occurence of each+  /// by the given projection), in the order of the first occurrence of each   /// unique element.+  ///+  /// In this example, `uniqued()` is used to include only names which are+  /// unique+  ///+  ///     let animals = ["dog", "pig", "cat", "ox", "dog","cat"]+  ///     let uniqued = animals.uniqued()+  ///     print(uniqued)+  ///     // Prints "["dog", "pig","cat", "ox"]"`+  ///+  /// - Parameter projection: A projecting closure. `projection` accepts an+  ///   element of this sequence as its parameter and returns a projected+  ///   value of the same  type.+  ///+  /// - Returns: Returns an array with only the unique elements of this sequence
  /// - Returns: An array with only the unique elements of this sequence, as
  ///   determined by the result of `projection` for each element.
Roshankumar350

comment created time in 6 days

PullRequestReviewEvent

push eventapple/swift-algorithms

Tim Vermeulen

commit sha 53354f3f217f6c6e67ff152d7b83d5ed6ca0f0c6

Add conditional LazySequenceProtocol conformance (#29) * Add conditional LazySequenceProtocol conformance where appropriate * Add laziness tests * Mention conditional conformance to LazySequenceProtocol in the guides

view details

push time in 8 days

PR merged apple/swift-algorithms

Add conditional LazySequenceProtocol conformance

<!-- Thanks for contributing to Swift Algorithms!

If this pull request adds new API, please add '?template=new.md'
to the URL to switch to the appropriate template.

Before you submit your request, please replace the paragraph
below with the relevant details, and complete the steps in the
checklist by placing an 'x' in each box:

- [x] I've completed this task
- [ ] This task isn't completed

-->

This adds overloads to LazySequenceProtocol of existing lazy-by-default sequence algorithms, for the sole purpose of keeping lazy chains lazy when one of these methods is called.

uniqued is left out of this because it (currently) isn't lazy to begin with. A LazySequenceProtocol overload will only make sense once we have a Uniqued sequence that produces unique elements on demand.

Two unresolved questions:

  • Do we want all of these overloads? I've added them for all algorithms that have a self and return a new sequence/collection, but that might be too lenient. (Is there a guideline for this somewhere?)
  • Should the guides mention these overloads, given that they don't add any actual functionality? The Chunked guide mentions them, but then again lazy.chunked and .chunked.lazy are entirely different things, unlike the ones I'm adding here. Mentioning them in a more general document such as the README is another option.

Checklist

  • [ ] I've added at least one test that validates that my change is working, if appropriate
  • [x] I've followed the code style of the rest of the project
  • [x] I've read the Contribution Guidelines
  • [x] I've updated the documentation if necessary
+55 -19

5 comments

17 changed files

timvermeulen

pr closed time in 8 days

pull request commentapple/swift-algorithms

Add conditional LazySequenceProtocol conformance

Looks great, @timvermeulen! 🎉

timvermeulen

comment created time in 8 days

issue commentapple/swift-argument-parser

Modifying help flag names does not work for subcommands

I would think an inheritance model would make the most sense, and would be compatible with the current system. So subcommands get whatever their super-commands have specified, unless they specify their own. I think this means that we’d need to change the property to be optional and have it default to nil, however.

paulofaria

comment created time in 8 days

pull request commentapple/swift-argument-parser

[Windows] use _dupenv_s instead of getenv

Thanks, @damuellen! Are you able to test this with one of the supported shells under Windows? I know we don't have automated tests for this, but it would be good to know the status.

damuellen

comment created time in 8 days

push eventapple/swift-algorithms

Nate Cook

commit sha 115abe1f69fee8946fa868e4c0323f1b17c231f2

Ignore Package.resolved

view details

Nate Cook

commit sha 8ca636d702f69f7a5e400b1d85472f00f77d26b6

Add inlinability annotations throughout

view details

push time in 8 days

Pull request review commentapple/swift-algorithms

Add Sequence.firstNonNil(of:)

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// firstNonNil(of:)+//===----------------------------------------------------------------------===//+public extension Sequence {+    /// Returns the first element in `self` that `transform` maps to a `.some`.+    func firstNonNil<Result>(of transform: (Element) throws -> Result?) rethrows -> Result? {

Agreed. I think we can drop the label — to me it reads better when it's more similar to map, e.g.: guard let number = inputs.firstNonNil(Int.init) else { ... }

Qata

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// Returns a collection stepping through the elements every `step` starting+  /// at the first value. Any remainders of the stride will be trimmed.+  ///+  ///     (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+  ///     (0...10).striding(by: 3) // == [0, 3, 6, 9]+  ///+  /// - Complexity: O(1). Access to successive values is O(1) if the+  /// collection conforms to `RandomAccessCollection`; otherwise,+  /// O(_k_), where _k_ is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride {+  public func striding(by step: Int) -> Self {+    Stride(base: base, stride: stride * step)+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  public var count: Int {+    (base.count - 1) / stride + 1+  }+  +  public func distance(from start: Index, to end: Index) -> Int {+    base.distance(from: start.base, to: end.base) / stride+  }+  +  public func index(_ i: Index, offsetBy distance: Int) -> Index {+    Index(base.index(i.base, offsetBy: distance * stride))+  }++}++extension Stride: BidirectionalCollection+  where Base: RandomAccessCollection {+  +  public func index(before i: Index) -> Index {+    precondition(i.base > base.startIndex, "Incrementing past start index")+    if i == endIndex {+      let count = base.count+      precondition(count > 0, "Can't move before the starting index")+      return Index(+        base.index(base.endIndex, offsetBy: -((count - 1) % stride + 1))+      )+    } else {+      guard let step = base.index(+        i.base,+        offsetBy: -stride,+        limitedBy: startIndex.base+      ) else {+        fatalError("Incrementing past start index")+      }+      return Index(step)+    }+  }+}++extension Stride: RandomAccessCollection+  where Base: RandomAccessCollection {}++extension Stride: Equatable+  where Base.Element: Equatable {+  +  public static func == (lhs: Stride, rhs: Stride) -> Bool {+    guard lhs.count == rhs.count else { return false }+    return lhs.elementsEqual(rhs, by: ==)+  }+  +}++extension Stride: Hashable+  where Base.Element: Hashable {+  +  public func hash(into hasher: inout Hasher) {+    hasher.combine(count)

You can leave the count out here, too.

ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++import XCTest+import Algorithms++final class StridingTests: XCTestCase {+  +  func testStride() {+    let a = (0...10)+    XCTAssertEqualSequences(a.striding(by: 1), (0...10))+    XCTAssertEqualSequences(a.striding(by: 2), [0, 2, 4, 6, 8, 10])+    XCTAssertEqualSequences(a.striding(by: 3), [0, 3, 6, 9])+    XCTAssertEqualSequences(a.striding(by: 4), [0, 4, 8])+    XCTAssertEqualSequences(a.striding(by: 5), [0, 5, 10])+    XCTAssertEqualSequences(a.striding(by: 10), [0, 10])+    XCTAssertEqualSequences(a.striding(by: 11), [0])+  }+  +  func testStrideString() {+    let s = "swift"+    XCTAssertEqualSequences(s.striding(by: 2), ["s", "i", "t"])+  }+  +  func testStrideReversed() {+    let a = [0, 1, 2, 3, 4, 5]+    XCTAssertEqualSequences(a.striding(by: 3).reversed(), [3, 0])+    XCTAssertEqualSequences(a.reversed().striding(by: 2), [5, 3, 1])+  }+  +  func testStrideIndexes() {+    let a = [0, 1, 2, 3, 4, 5].striding(by: 2)+    var i = a.startIndex+    XCTAssertEqual(a[i], 0)+    a.formIndex(after: &i)+    XCTAssertEqual(a[i], 2)+    a.formIndex(after: &i)+    XCTAssertEqual(a[i], 4)+    a.formIndex(before: &i)+    XCTAssertEqual(a[i], 2)+    a.formIndex(before: &i)+    XCTAssertEqual(a[i], 0)+//    a.formIndex(before: &i) // Precondition failed: Advancing past start index+//    a.index(after: a.endIndex) // Precondition failed: Advancing past end index+  }+  +  func testStrideCompositionEquivalence() {+    let a = (0...10)+    XCTAssertEqualSequences(a.striding(by: 6), a.striding(by: 2).striding(by: 3))+    XCTAssertTrue(a.striding(by: 6) == a.striding(by: 2).striding(by: 3))+    XCTAssert(type(of: a.striding(by: 2).striding(by: 3)) == Stride<ClosedRange<Int>>.self)+  }+  +  func testEquality() {+    let a = [1, 2, 3, 4, 5].striding(by: 2)+    let b = [1, 0, 3, 0, 5].striding(by: 2)+    XCTAssertEqual(a, b)+  }+  +  func testStrideLast() {+    XCTAssertEqual((1...10).striding(by: 2).last, 9) // 1, 3, 5, 7, 9+    XCTAssertEqual((1...10).striding(by: 3).last, 10) // 1, 4, 7, 10+    XCTAssertEqual((1...10).striding(by: 4).last, 9) // 1, 5, 9+    XCTAssertEqual((1...10).striding(by: 5).last, 6) // 1, 6+    XCTAssertEqual((1...100).striding(by: 50).last, 51) // 1, 51+    XCTAssertEqual((1...5).striding(by: 2).last, 5) // 1, 3, 5+    XCTAssertEqual([Int]().striding(by: 2).last, nil) // empty+  }+  +  func testCount() {+    let a = (0...10)+    XCTAssertEqual(a.striding(by: 1).count, (0...10).count)+    XCTAssertEqual(a.striding(by: 2).count, [0, 2, 4, 6, 8, 10].count)+    XCTAssertEqual(a.striding(by: 3).count, [0, 3, 6, 9].count)+    XCTAssertEqual(a.striding(by: 4).count, [0, 4, 8].count)+    XCTAssertEqual(a.striding(by: 5).count, [0, 5, 10].count)+    XCTAssertEqual(a.striding(by: 10).count, [0, 10].count)+    XCTAssertEqual(a.striding(by: 11).count, [0].count)+  }+  +  func testDistance() {+    let a = (0...100).striding(by: 12)+    var i = a.startIndex+    a.formIndex(&i, offsetBy: 3)+    XCTAssertEqual(a.distance(from: a.startIndex, to: i), 3)+    XCTAssertEqual(a[i], 36)+    XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count - 1)

Here too:

    XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count)
ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// Returns a collection stepping through the elements every `step` starting+  /// at the first value. Any remainders of the stride will be trimmed.+  ///+  ///     (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+  ///     (0...10).striding(by: 3) // == [0, 3, 6, 9]+  ///+  /// - Complexity: O(1). Access to successive values is O(1) if the+  /// collection conforms to `RandomAccessCollection`; otherwise,+  /// O(_k_), where _k_ is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride {+  public func striding(by step: Int) -> Self {+    Stride(base: base, stride: stride * step)+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  public var count: Int {+    (base.count - 1) / stride + 1+  }+  +  public func distance(from start: Index, to end: Index) -> Int {+    base.distance(from: start.base, to: end.base) / stride+  }+  +  public func index(_ i: Index, offsetBy distance: Int) -> Index {+    Index(base.index(i.base, offsetBy: distance * stride))+  }++}++extension Stride: BidirectionalCollection+  where Base: RandomAccessCollection {+  +  public func index(before i: Index) -> Index {+    precondition(i.base > base.startIndex, "Incrementing past start index")+    if i == endIndex {+      let count = base.count+      precondition(count > 0, "Can't move before the starting index")+      return Index(+        base.index(base.endIndex, offsetBy: -((count - 1) % stride + 1))+      )+    } else {+      guard let step = base.index(+        i.base,+        offsetBy: -stride,+        limitedBy: startIndex.base+      ) else {+        fatalError("Incrementing past start index")+      }+      return Index(step)+    }+  }+}++extension Stride: RandomAccessCollection+  where Base: RandomAccessCollection {}++extension Stride: Equatable+  where Base.Element: Equatable {+  +  public static func == (lhs: Stride, rhs: Stride) -> Bool {+    guard lhs.count == rhs.count else { return false }

You can leave this check out — elementsEqual will fail if they have different lengths.

ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// Returns a collection stepping through the elements every `step` starting+  /// at the first value. Any remainders of the stride will be trimmed.+  ///+  ///     (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+  ///     (0...10).striding(by: 3) // == [0, 3, 6, 9]+  ///+  /// - Complexity: O(1). Access to successive values is O(1) if the+  /// collection conforms to `RandomAccessCollection`; otherwise,+  /// O(_k_), where _k_ is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride {+  public func striding(by step: Int) -> Self {+    Stride(base: base, stride: stride * step)+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  public var count: Int {+    (base.count - 1) / stride + 1+  }+  +  public func distance(from start: Index, to end: Index) -> Int {+    base.distance(from: start.base, to: end.base) / stride+  }+  +  public func index(_ i: Index, offsetBy distance: Int) -> Index {+    Index(base.index(i.base, offsetBy: distance * stride))

Similar issue — this can overrun base.endIndex in a way that will trap in some collections when it should just return endIndex. I think this will have the problem:

let a = 1...5
let b = a.striding(by: 3) // equivalent to [1, 4]
let i = b.index(b.startIndex, offsetBy: 2)
ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++import XCTest+import Algorithms++final class StridingTests: XCTestCase {+  +  func testStride() {+    let a = (0...10)+    XCTAssertEqualSequences(a.striding(by: 1), (0...10))+    XCTAssertEqualSequences(a.striding(by: 2), [0, 2, 4, 6, 8, 10])+    XCTAssertEqualSequences(a.striding(by: 3), [0, 3, 6, 9])+    XCTAssertEqualSequences(a.striding(by: 4), [0, 4, 8])+    XCTAssertEqualSequences(a.striding(by: 5), [0, 5, 10])+    XCTAssertEqualSequences(a.striding(by: 10), [0, 10])+    XCTAssertEqualSequences(a.striding(by: 11), [0])+  }+  +  func testStrideString() {+    let s = "swift"+    XCTAssertEqualSequences(s.striding(by: 2), ["s", "i", "t"])+  }+  +  func testStrideReversed() {+    let a = [0, 1, 2, 3, 4, 5]+    XCTAssertEqualSequences(a.striding(by: 3).reversed(), [3, 0])+    XCTAssertEqualSequences(a.reversed().striding(by: 2), [5, 3, 1])+  }+  +  func testStrideIndexes() {+    let a = [0, 1, 2, 3, 4, 5].striding(by: 2)+    var i = a.startIndex+    XCTAssertEqual(a[i], 0)+    a.formIndex(after: &i)+    XCTAssertEqual(a[i], 2)+    a.formIndex(after: &i)+    XCTAssertEqual(a[i], 4)+    a.formIndex(before: &i)+    XCTAssertEqual(a[i], 2)+    a.formIndex(before: &i)+    XCTAssertEqual(a[i], 0)+//    a.formIndex(before: &i) // Precondition failed: Advancing past start index+//    a.index(after: a.endIndex) // Precondition failed: Advancing past end index+  }+  +  func testStrideCompositionEquivalence() {+    let a = (0...10)+    XCTAssertEqualSequences(a.striding(by: 6), a.striding(by: 2).striding(by: 3))+    XCTAssertTrue(a.striding(by: 6) == a.striding(by: 2).striding(by: 3))+    XCTAssert(type(of: a.striding(by: 2).striding(by: 3)) == Stride<ClosedRange<Int>>.self)+  }+  +  func testEquality() {+    let a = [1, 2, 3, 4, 5].striding(by: 2)+    let b = [1, 0, 3, 0, 5].striding(by: 2)+    XCTAssertEqual(a, b)+  }+  +  func testStrideLast() {+    XCTAssertEqual((1...10).striding(by: 2).last, 9) // 1, 3, 5, 7, 9+    XCTAssertEqual((1...10).striding(by: 3).last, 10) // 1, 4, 7, 10+    XCTAssertEqual((1...10).striding(by: 4).last, 9) // 1, 5, 9+    XCTAssertEqual((1...10).striding(by: 5).last, 6) // 1, 6+    XCTAssertEqual((1...100).striding(by: 50).last, 51) // 1, 51+    XCTAssertEqual((1...5).striding(by: 2).last, 5) // 1, 3, 5+    XCTAssertEqual([Int]().striding(by: 2).last, nil) // empty+  }+  +  func testCount() {+    let a = (0...10)+    XCTAssertEqual(a.striding(by: 1).count, (0...10).count)+    XCTAssertEqual(a.striding(by: 2).count, [0, 2, 4, 6, 8, 10].count)+    XCTAssertEqual(a.striding(by: 3).count, [0, 3, 6, 9].count)+    XCTAssertEqual(a.striding(by: 4).count, [0, 4, 8].count)+    XCTAssertEqual(a.striding(by: 5).count, [0, 5, 10].count)+    XCTAssertEqual(a.striding(by: 10).count, [0, 10].count)+    XCTAssertEqual(a.striding(by: 11).count, [0].count)

Ah — yes:

    XCTAssertEqual(a.striding(by: 11).count, 0)
ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// Returns a collection stepping through the elements every `step` starting+  /// at the first value. Any remainders of the stride will be trimmed.+  ///+  ///     (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+  ///     (0...10).striding(by: 3) // == [0, 3, 6, 9]+  ///+  /// - Complexity: O(1). Access to successive values is O(1) if the+  /// collection conforms to `RandomAccessCollection`; otherwise,+  /// O(_k_), where _k_ is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride {+  public func striding(by step: Int) -> Self {+    Stride(base: base, stride: stride * step)+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  public var count: Int {+    (base.count - 1) / stride + 1+  }+  +  public func distance(from start: Index, to end: Index) -> Int {+    base.distance(from: start.base, to: end.base) / stride

This can have an off by one error when end == endIndex and base.count isn’t a multiple of stride.

ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// Returns a collection stepping through the elements every `step` starting+  /// at the first value. Any remainders of the stride will be trimmed.+  ///+  ///     (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+  ///     (0...10).striding(by: 3) // == [0, 3, 6, 9]+  ///+  /// - Complexity: O(1). Access to successive values is O(1) if the+  /// collection conforms to `RandomAccessCollection`; otherwise,+  /// O(_k_), where _k_ is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride {+  public func striding(by step: Int) -> Self {+    Stride(base: base, stride: stride * step)+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  public var count: Int {+    (base.count - 1) / stride + 1

I don’t think this works when the collection is empty — (0 - 1) / stride + 1 is going to be 1 when stride > 1.

ollieatkinson

comment created time in 9 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentapple/swift-algorithms

Add overloads to LazySequenceProtocol that maintain laziness

That sounds like a good solution for cycled(times:) — let’s handle that as a separate change. I didn’t realize that FlattenSequence doesn’t forward down to its base collection’s index offsetting methods. Seems like something that we could improve!

timvermeulen

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// - Complexity: O(_1_) and access to the next value in the striding+  /// is O(_1_) if the collection conforms to `RandomAccessCollection`,+  /// otherwise O(_k_) where `k` is the striding `step`.
  /// - Complexity: O(1). Access to successive values is O(1) if the
  /// collection conforms to `RandomAccessCollection`; otherwise,
  /// O(_k_), where _k_ is the striding `step`.

Also, the documentation should start with a single sentence describing what the method returns.

ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++import XCTest+import Algorithms++final class StridingTests: XCTestCase {+  +  func testStride() {+    let a = (0...10)+    XCTAssertEqualSequences(a.striding(by: 1), (0...10))+    XCTAssertEqualSequences(a.striding(by: 2), [0, 2, 4, 6, 8, 10])+    XCTAssertEqualSequences(a.striding(by: 3), [0, 3, 6, 9])+    XCTAssertEqualSequences(a.striding(by: 4), [0, 4, 8])+    XCTAssertEqualSequences(a.striding(by: 5), [0, 5, 10])+    XCTAssertEqualSequences(a.striding(by: 10), [0, 10])+    XCTAssertEqualSequences(a.striding(by: 11), [0])+  }+  +  func testStrideString() {+    let s = "swift"+    XCTAssertEqualSequences(s.striding(by: 2), ["s", "i", "t"])+  }+  +  func testStrideReversed() {+    let a = [0, 1, 2, 3, 4, 5]+    XCTAssertEqualSequences(a.striding(by: 3).reversed(), [3, 0])+    XCTAssertEqualSequences(a.reversed().striding(by: 2), [5, 3, 1])+  }+  +  func testStrideIndexes() {+    let a = [0, 1, 2, 3, 4, 5].striding(by: 2)+    var i = a.startIndex+    XCTAssertEqual(a[i], 0)+    a.formIndex(after: &i)+    XCTAssertEqual(a[i], 2)+    a.formIndex(after: &i)+    XCTAssertEqual(a[i], 4)+    a.formIndex(before: &i)+    XCTAssertEqual(a[i], 2)+    a.formIndex(before: &i)+    XCTAssertEqual(a[i], 0)+//    a.formIndex(before: &i) // Precondition failed: Advancing past start index+//    a.index(after: a.endIndex) // Precondition failed: Advancing past end index+  }+  +  func testStrideCompositionEquivalence() {+    let a = (0...10)+    XCTAssertEqualSequences(a.striding(by: 6), a.striding(by: 2).striding(by: 3))+  }+  +  func testStrideLast() {+    XCTAssertEqual((1...10).striding(by: 2).last, 9) // 1, 3, 5, 7, 9+    XCTAssertEqual((1...10).striding(by: 3).last, 10) // 1, 4, 7, 10+    XCTAssertEqual((1...10).striding(by: 4).last, 9) // 1, 5, 9+    XCTAssertEqual((1...10).striding(by: 5).last, 6) // 1, 6+    XCTAssertEqual((1...100).striding(by: 50).last, 51) // 1, 51+    XCTAssertEqual((1...5).striding(by: 2).last, 5) // 1, 3, 5+    XCTAssertEqual([Int]().striding(by: 2).last, nil) // 1, 3, 5
    XCTAssertEqual([Int]().striding(by: 2).last, nil) // empty
ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// - Complexity: O(_1_) and access to the next value in the striding+  /// is O(_1_) if the collection conforms to `RandomAccessCollection`,+  /// otherwise O(_k_) where `k` is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  // TODO: Implement distance(from:to:) and index(_:offsetBy:)++}++extension Stride: BidirectionalCollection+  where Base: RandomAccessCollection {+  +  public func index(before i: Index) -> Index {+    precondition(i.base > base.startIndex, "Incrementing past start index")+    if i == endIndex {+      let count = base.count+      precondition(count > 0, "Incrementing when count is zero")+      return Index(+        base.index(base.endIndex, offsetBy: -((count - 1) % stride + 1))+      )+    } else {+      return base.index(i.base, offsetBy: -stride, limitedBy: startIndex.base)+        .map(Index.init) ?? startIndex+    }+  }+}++extension Stride: RandomAccessCollection+  where Base: RandomAccessCollection {}++extension Stride: Equatable+  where Base: Equatable {}++extension Stride: Hashable+  where Base: Hashable, Base.Index: Hashable {}

Note 2: The Base.Index constraint isn't necessary here, but we should add:

extension Stride.Index: Hashable where Base.Index: Hashable {}
ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// - Complexity: O(_1_) and access to the next value in the striding+  /// is O(_1_) if the collection conforms to `RandomAccessCollection`,+  /// otherwise O(_k_) where `k` is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  // TODO: Implement distance(from:to:) and index(_:offsetBy:)

Probably a good idea to provide an implementation of count here as well, so that you can use the faster version for collections that customize that.

ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// - Complexity: O(_1_) and access to the next value in the striding+  /// is O(_1_) if the collection conforms to `RandomAccessCollection`,+  /// otherwise O(_k_) where `k` is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  // TODO: Implement distance(from:to:) and index(_:offsetBy:)++}++extension Stride: BidirectionalCollection+  where Base: RandomAccessCollection {+  +  public func index(before i: Index) -> Index {+    precondition(i.base > base.startIndex, "Incrementing past start index")+    if i == endIndex {+      let count = base.count+      precondition(count > 0, "Incrementing when count is zero")
      precondition(count > 0, "Can't move before the starting index")
ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// - Complexity: O(_1_) and access to the next value in the striding+  /// is O(_1_) if the collection conforms to `RandomAccessCollection`,+  /// otherwise O(_k_) where `k` is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  // TODO: Implement distance(from:to:) and index(_:offsetBy:)++}++extension Stride: BidirectionalCollection+  where Base: RandomAccessCollection {+  +  public func index(before i: Index) -> Index {+    precondition(i.base > base.startIndex, "Incrementing past start index")+    if i == endIndex {+      let count = base.count+      precondition(count > 0, "Incrementing when count is zero")+      return Index(+        base.index(base.endIndex, offsetBy: -((count - 1) % stride + 1))+      )+    } else {+      return base.index(i.base, offsetBy: -stride, limitedBy: startIndex.base)+        .map(Index.init) ?? startIndex+    }+  }+}++extension Stride: RandomAccessCollection+  where Base: RandomAccessCollection {}++extension Stride: Equatable+  where Base: Equatable {}++extension Stride: Hashable+  where Base: Hashable, Base.Index: Hashable {}

This type is an interesting case for Equatable conformance — should the presence of the base property mean that two collections whose elements compare as equal are actually not equal? That is, does this print "equal"?

let a = [1, 2, 3, 4, 5].striding(by: 2)
let b = [1, 0, 3, 0, 5].striding(by: 2)
if a == b {
    print("equal")
}

In this case we should treat a and b as equal, despite the fact that their base collections are not. We have a variety of ways that "equal" instances can be distinguished — class identity, the sign on floating-point zero, etc — and adhering too closely to Equatable's substitutability requirement can lead to surprising results like a false result above.

In this case, that will mean that you need to implement the == operator and the hash(into:) method manually, so that the base collection won't be included in their calculation.

ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+# Stride++[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Stride.swift) | + [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/StrideTests.swift)]++A type that steps over a collection’s elements by the specified amount.++This is available through the `striding(by:)` method on any `Collection`.++```swift+(0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10]+```++If the stride is larger than the collection count, the resulting wrapper only contains the +first element.++The stride amount must be a positive value.++## Detailed Design++The `striding(by:)` method is declared as a `Collection` extension, and returns a +`Stride` type:++```swift+extension Collection {+  public func striding(by step: Int) -> Stride<Self>+}+```++A custom `Index` type is defined so that it's not possible to get confused when trying +to access an index of the stride collection.++```swift+[0, 1, 2, 3, 4].striding(by: 2)[1] // == 1+[0, 1, 2, 3, 4].striding(by: 2).map { $0 }[1] // == 2+```++A careful thought was given to the composition of these strides by giving a custom +implementation to `index(_:offsetBy:limitedBy)` which multiplies the offset by the +stride amount. ++```swift+base.index(i.base, offsetBy: distance * stride, limitedBy: base.endIndex)+```++The following two lines of code are equivalent, including performance:++```swift+(0...10).striding(by: 6)+(0...10).striding(by: 2).stride(by: 3)

What would you think about adding a striding(by:) overload to the Stride type, such that (0...10).striding(by: 2).striding(by: 3) would be a Stride<ClosedRange<Int>> instead of a Stride<Stride<ClosedRange<Int>>>?

ollieatkinson

comment created time in 9 days

Pull request review commentapple/swift-algorithms

Add Stride for Collection

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// striding(by:)+//===----------------------------------------------------------------------===//++extension Collection {+  /// - Complexity: O(_1_) and access to the next value in the striding+  /// is O(_1_) if the collection conforms to `RandomAccessCollection`,+  /// otherwise O(_k_) where `k` is the striding `step`.+  ///+  /// - Parameter step: The amount to step with each iteration.+  /// - Returns: Returns a collection stepping through the elements by the+  /// specified amount.+  public func striding(by step: Int) -> Stride<Self> {+    Stride(base: self, stride: step)+  }+}++public struct Stride<Base: Collection> {+  +  public let base: Base+  public let stride: Int+  +  init(base: Base, stride: Int) {+    precondition(stride > 0, "striding must be greater than zero")+    self.base = base+    self.stride = stride+  }+}++extension Stride: Collection {+  +  public struct Index: Comparable {+    +    let base: Base.Index+    +    init(_ base: Base.Index) {+      self.base = base+    }+    +    public static func < (lhs: Index, rhs: Index) -> Bool {+      lhs.base < rhs.base+    }+  }+  +  public var startIndex: Index {+    Index(base.startIndex)+  }+  +  public var endIndex: Index {+    Index(base.endIndex)+  }+  +  public subscript(i: Index) -> Base.Element {+    base[i.base]+  }+  +  public func index(after i: Index) -> Index {+    precondition(i.base < base.endIndex, "Advancing past end index")+    return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)+      .map(Index.init) ?? endIndex+  }+  +  public func index(+    _ i: Index,+    offsetBy distance: Int,+    limitedBy limit: Index+  ) -> Index? {+    base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)+      .map(Index.init)+  }+  +  // TODO: Implement distance(from:to:) and index(_:offsetBy:)++}++extension Stride: BidirectionalCollection+  where Base: RandomAccessCollection {+  +  public func index(before i: Index) -> Index {+    precondition(i.base > base.startIndex, "Incrementing past start index")+    if i == endIndex {+      let count = base.count+      precondition(count > 0, "Incrementing when count is zero")+      return Index(+        base.index(base.endIndex, offsetBy: -((count - 1) % stride + 1))+      )+    } else {+      return base.index(i.base, offsetBy: -stride, limitedBy: startIndex.base)+        .map(Index.init) ?? startIndex

I would think this should fail if it moves past startIndex, not return a valid index. (This should be an impossibility, however.)

ollieatkinson

comment created time in 9 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentapple/swift-algorithms

Add "partiallySorted(count:)" to Collection

@rockbruno Fascinating plots! I think in those last two we're seeing the m^2 performance of repeated insertions swamp the rest of the n log m algorithm, which eventually does worse than the n log n sort of the full collection. I think the optimization you're talking about makes sense, since getting a relatively small number is going to be the most common usage.

Do we agree in returning an ArraySlice in all methods then?

We should be able to return an Array from all the nonmutating methods.

rakaramos

comment created time in 9 days

push eventapple/swift-algorithms

Meler Paine

commit sha 0dba0e5b87b137905d72a97f3284b173b7a0cab0

fix stablePartition(subrange:by:) using subrange count instead of entire collection count. (#30)

view details

push time in 9 days

PR merged apple/swift-algorithms

Fix stablePartition(subrange:by:) using subrange count instead of entire collection count.

Function stablePartition(subrange:by:) in Partition should not using the collection count which will cause bound error, but using subrange count.

Checklist

  • [x] I've added at least one test that validates that my change is working, if appropriate
  • [x] I've followed the code style of the rest of the project
  • [x] I've read the Contribution Guidelines
  • [ ] I've updated the documentation if necessary
+16 -1

2 comments

2 changed files

pmtao

pr closed time in 9 days

pull request commentapple/swift-algorithms

Fix stablePartition(subrange:by:) using subrange count instead of entire collection count.

Thanks for this fix, @pmtao! :clap::clap::clap:

pmtao

comment created time in 9 days

push eventapple/swift-argument-parser

Saleem Abdulrasool

commit sha 8492882b030ad1c8e0bb4ca9d9ce06b07a8150b2

Windows: migrate to `CRT` from `MSVCRT` (#249) The Windows environment calls the library `CRT`. This also enables the removal of the `visualc` module from the Swift SDK overlay.

view details

push time in 9 days

PR merged apple/swift-argument-parser

Windows: migrate to `CRT` from `MSVCRT`

The Windows environment calls the library CRT. This also enables the removal of the visualc module from the Swift SDK overlay.

<!-- Thanks for contributing to the Swift Argument Parser!

If this pull request adds new API, please add '?template=new.md'
to the URL to switch to the appropriate template.

Before you submit your request, please replace the paragraph
below with the relevant details, and complete the steps in the
checklist by placing an 'x' in each box:

- [x] I've completed this task
- [ ] This task isn't completed

-->

Replace this paragraph with a description of your changes and rationale. Provide links to an existing issue or external references/discussions, if appropriate.

Checklist

  • [x] I've added at least one test that validates that my change is working, if appropriate
  • [x] I've followed the code style of the rest of the project
  • [x] I've read the Contribution Guidelines
  • [x] I've updated the documentation if necessary
+8 -8

3 comments

4 changed files

compnerd

pr closed time in 9 days

Pull request review commentapple/swift-algorithms

Add BidirectionalCollection.trimmed

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++extension BidirectionalCollection {++  /// Returns a `SubSequence` formed by discarding all elements at the start and end of the Collection+  /// which satisfy the given predicate.+  ///+  /// Trimming is a common operation in computing, often referring to removing leading and trailing whitespace+  /// from a string[1].

I don't think we need this explanation. Let's introduce the example, instead — something like:

This example uses trimmed(where:) to get a substring without the white space at the beginning and end of the string.

karwa

comment created time in 10 days

Pull request review commentapple/swift-algorithms

Add BidirectionalCollection.trimmed

+//===----------------------------------------------------------------------===//+//+// This source file is part of the Swift Algorithms open source project+//+// Copyright (c) 2020 Apple Inc. and the Swift project authors+// Licensed under Apache License v2.0 with Runtime Library Exception+//+// See https://swift.org/LICENSE.txt for license information+//+//===----------------------------------------------------------------------===//++extension BidirectionalCollection {++  /// Returns a `SubSequence` formed by discarding all elements at the start and end of the Collection+  /// which satisfy the given predicate.+  ///+  /// Trimming is a common operation in computing, often referring to removing leading and trailing whitespace+  /// from a string[1].+  ///+  ///  ```+  ///  let myString = "  hello, world  "+  ///  print(myString.trimmed(where: \.isWhitespace)) // "hello, world"+  ///  ```+  ///+  /// [1]: https://en.wikipedia.org/wiki/Trimming_(computer_programming)

We should mention that if the entire collection is trimmed, the result is an empty subsequence at the end of the collection.

karwa

comment created time in 10 days

Pull request review commentapple/swift-algorithms

Add BidirectionalCollection.trimmed

+# Trim++[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Trim.swift) |+ [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/TrimTests.swift)]++Returns a `SubSequence` formed by discarding all elements at the start and end of the Collection+which satisfy the given predicate.++```swift+let results = [2, 10, 11, 15, 20, 21, 100].trimmed(where: { $0.isMultiple(of: 2) })+print(results) // [11, 15, 20, 21]++let myString = "   hello, world  "+print(myString.trimmed(where: \.isWhitespace)) // "hello, world"+```++Trimming is a common operation in computing. It is particularly for String processing, but also useful in+other applications; for example, a signal processing library might want to trim leading and trailing `0` values+to "zoom in" on the window of data that contains useful values.++## Detailed Design++A new method is added to `BidirectionalCollection`:++```swift+extension BidirectionalCollection {++  public func trimmed(where predicate: (Element) throws -> Bool) rethrows -> SubSequence+}+```++This method requires `BidirectionalCollection` for an efficient implementation which visits as few elements as possible.++A less-efficient implementation is _possible_ for any `Collection`, which would involve always traversing the+entire collection. This implementation is not provided, as it would mean developers of generic algorithms who forget+to add the `BidirectionalCollection` constraint will recieve that inefficient implementation:

Agreed, BidirectionalCollection is the right place for this.

karwa

comment created time in 10 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentapple/swift-algorithms

Add "partiallySorted(count:)" to Collection

@pyrtsa sortedPrefix is a good name for this! I had been thinking about something like min(5, by: <), but that doesn't indicate that the result is sorted, and would require max(5, by: <) for parity.

I can see the justification for the mutating partial sort, but I don't think I see it for a version that returns the whole array with only part of it sorted. What's the use case there?

rakaramos

comment created time in 10 days

pull request commentapple/swift-algorithms

Add overloads to LazySequenceProtocol that maintain laziness

This is a great question, @timvermeulen. The goal as I understand it is to avoid requiring people to write .lazy after operations on lazy collections to make sure their collection stays lazy. As far as I can tell, the stdlib takes two different approaches. The one you've taken here is to apply the operation to self.elements, and then call .lazy on the result, which matches the behavior of FlattenSequence:

let a = (1...10).lazy
let b = a.map { $0...20 }.joined()
// b is a LazySequence<FlattenSequence<LazyMapSequence<LazySequence<(ClosedRange<Int>)>.Elements, ClosedRange<LazySequence<(ClosedRange<Int>)>.Element>>.Elements>>

ReversedCollection, on the other hand, conditionally conforms to the lazy protocols when its base collection does:

let c = a.reversed()
// c is a ReversedCollection<LazySequence<(ClosedRange<Int>)>>

This second approach seems cleaner to me (and could have been applied to FlattenSequence) — would it work in the case of this algorithms library?

Re, the guides: I think they should mention either the overloads or the conditional conformance in the detailed design section. It would be great to also have a test that verifies that the laziness passes through properly (with a fatalError-ing map, perhaps).

timvermeulen

comment created time in 10 days

push eventapple/swift-algorithms

Stephen Canon

commit sha a0b7954f80d4cbed1422db8bf75f26ac3d221fd0

Use Swift Numerics for elementary functions instead of Darwin/Glibc/etc (#28) Also lets us use root instead of exp(log(x)/k), which is more accurate in some cases (but mainly is just nicer to read). The downside is that this introduces a dependency for Algorithms, where previously it had none. I think that Numerics is an OK thing to depend on (especially for these functions, which are available from 0.0.1), but it _is_ a _massive increase_ in the number of dependencies that the package has, and we should give that some thought.

view details

push time in 10 days

PR merged apple/swift-algorithms

Reviewers
Use Swift Numerics for elementary functions

This drops the canImport darwin/glibc/etc dance, which is ugly, and lets us use root instead of exp(log(x)/k), which is more accurate in some cases (but mainly is just nicer to read).

The downside is that this introduces a dependency for Algorithms, where previously it had none. I think that Numerics is an OK thing to depend on (especially for these functions, which are available from 0.0.1), but it is a massive increase in the number of dependencies that the package has, and we should give that some thought.

+12 -19

3 comments

2 changed files

stephentyrone

pr closed time in 10 days

pull request commentapple/swift-algorithms

Use Swift Numerics for elementary functions

(c) If we ever want to get the same results across platforms for a given PRNG (which I expect we will), the only path to that is via Numerics. We could choose to provide portable log and root operations there, but the Darwin/Glibc/etc operations will never give the same results everywhere.

Sold on this point!

stephentyrone

comment created time in 10 days

pull request commentapple/swift-algorithms

Use Swift Numerics for elementary functions

I'm definitely reticent to add this due to the, as you say, massive increase in dependencies. It seems like there's a trade-off at some point — if Algorithms were to use more of the higher-level functionality, or the generic capability that Swift Numerics provides, it would make sense to bring in the dependency, but at this point it isn't worth the addition. What do you think?

stephentyrone

comment created time in 10 days

push eventapple/swift-algorithms

Stephen Canon

commit sha 73c4a2156347366818ee6f7914bfdebc1239b079

Add the magic plist file to make xcode render the markdown guides. (#27)

view details

push time in 13 days

PR merged apple/swift-algorithms

Add the magic plist file to make xcode render the markdown guides.

The downside to doing this is that it makes the files more inconvenient to edit, since Xcode doesn't let you toggle out of the rendered view easily, but I think it makes sense on the premise that these are read much more often than edited. I have this flag set in Swift Numerics for this reason.

+5 -0

0 comment

1 changed file

stephentyrone

pr closed time in 13 days

issue commentapple/swift-algorithms

Consider adding scan

Thanks, @fwgreen — this looks like a good addition, especially since it's easy to get the implementation wrong. 👏👏 Let's go with reductions(_:_:) for the name.

fwgreen

comment created time in 14 days

issue commentapple/swift-algorithms

A `first` operator that returns the first non-nil transformed value

The name is definitely the trickiest part here, but I think we can add this with our best bet and move forward that way. Let's start with firstNonNil(_:) and go from there. Thanks!

Qata

comment created time in 14 days

more