2

我正在尝试添加与ForwardIndexTypeinNSDate的一致性,以便我可以制作一个Range<NSDate>,为了做到这一点,我必须实现public func successor() -> Selffrom _Incrementable

我的实现非常简单,旨在说明另一个成功的日期是恰好在它之后一秒的日期,这不是这里要问的。

extension NSDate: ForwardIndexType {
    public func successor() -> Self {
        return NSDate(timeInterval: 1, sinceDate: self) 
    }
}

我得到的错误是

无法将类型“ ”的返回表达式转换NSDate为返回类型“ Self

我尝试添加as Selforas! Self但编译器不允许我,因为在这种情况下转换 fromNSDate总是Self成功。替换SelfNSDate也不起作用。

我怎样才能以正确的方式做到这一点?

4

2 回答 2

2

正如几条评论所说,即使可能,您也不应该这样做。NSDate没有合乎逻辑的“下一个”。发明一种可能会产生副作用。公共类型的扩展是全局的。如果您说“下一个日期是下一秒”而另一个扩展名说“下一个日期是第二天”会发生什么?两者都同样合理(同样不正确)。如果其他人也这样做,切勿添加可能与不同含义发生冲突的扩展名。

你说你的目标是:

我想在给定的时间间隔内创建一组 n 个随机日期。我想打乱一个范围并选择前 n 个值

这完全没有问题。首先,正如您所说,您想要“在给定的时间间隔内”。出色的。那是一个ClosedInterval<NSDate>。要做到这一点,NSDate必须是可比的。添加该扩展名没有任何问题。任何合理实施它的人都必须以这种方式实施。

extension NSDate: Comparable {}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == NSComparisonResult.OrderedAscending
}

现在您想将其转换为整数秒范围,而不是日期范围。然后打乱该范围内的元素,提取第一个n值,并将它们映射回日期。我们假设您已经拥有 Nate Cook 的shuffle code

func randomDatesInInterval<DateInterval: IntervalType where DateInterval.Bound == NSDate>
    (interval: DateInterval, count: Int) -> [NSDate] {
    // For convenience we're going to assume that the range is no larger than 68 years.
    // If you need ranges larger than that, it's a bit more work and probably the subject
    // of a second question. (See https://stackoverflow.com/a/34388108/97337 for the basis.)
    let intervalSize = UInt32(interval.end.timeIntervalSinceDate(interval.start))

    let offsets = (0...intervalSize).shuffle()

    return Array(offsets.map { interval.start.dateByAddingTimeInterval(NSTimeInterval($0)) }.prefix(count))
}

你甚至可以使用它.....<定义你的间隔:

// 100 second-interval dates from the last hour
randomDatesInInterval(NSDate(timeIntervalSinceNow: -3600)...NSDate(), count: 100)
    .forEach { print($0) }

n请注意,如果该算法明显小于间隔中的秒数,则此算法有点慢且内存密集。我们必须创建一个非常庞大的数字数组才能按照您的要求进行操作。如果您不关心重复,那么一切都会简单得多:

let intervalSize = UInt32(interval.end.timeIntervalSinceDate(interval.start))

return (1...count).map { _ in
    let offset = arc4random_uniform(intervalSize)
    return interval.start.dateByAddingTimeInterval(Double(offset))
}

如果间隔显着大于n,则重复的机会很低。如果您仍然想避免重复而不必分配那个巨大的初始数组,请考虑Set

func randomDatesInInterval<DateInterval: IntervalType where DateInterval.Bound == NSDate>
    (interval: DateInterval, count: Int) -> [NSDate] {
        let intervalSize = UInt32(interval.end.timeIntervalSinceDate(interval.start))

        var offsets = Set<UInt32>()
        while offsets.count < count {
            offsets.insert(arc4random_uniform(intervalSize))
        }

        return offsets.sort().map { interval.start.dateByAddingTimeInterval(NSTimeInterval($0)) }
}

a 的权衡是,如果与间隔中的秒数具有相似的量级,则Set此方法非常慢。n在这种情况下,洗牌效率更高。

于 2016-01-06T18:38:06.440 回答
0

试试这个,更改SelfNSDate,删除ForwardIndexType协议

    extension NSDate: _Incrementable {
    public func successor() -> Self {
        return self.dynamicType.init(timeInterval: 1, sinceDate: self)
    }
}

在代码中使用,例如,

var date = NSDate()
date = date.successor()

let newDate = date.successor()

ForwardIndexType协议中,有类似的方法,advancedBy:并且distanceTo:,您正在实现方法successor,只需实现协议_Incrementable。这会奏效。

于 2016-01-06T12:56:32.603 回答