按哈希值访问Swift集元素

时间:2015-12-22 22:34:43

标签: swift hash set

假设我正在制作一个国际象棋应用程序,其中存储的位置如下:

struct Position {
    var pieces: Set<Piece>
    // some other stuff
}

Piece定义如下:

struct Piece: Hashable {
    var row: Int
    var column: Int
    let player: Player
    let type: PieceType

    var hashValue: Int {
        return 8 * row + column
    }
}

PlayerPieceType是简单的枚举:

enum Player {
    case White, Black
}

enum PieceType {
    case Queen, King, ...
}

现在,我想通过其在Piece中的位置访问PositionPiece的哈希值由其位置唯一确定,因此应该可以在恒定时间内访问Piece。但是,Swift集合没有通过其哈希值获取其元素之一的函数。我能想到的只是

for piece in pieces {
    if piece.hashValue == 25 {
        // do something
    }
}

......但显然,这是在线性时间内运行,而不是在恒定时间内运行。

解决此问题的一种方法是不使用集合,而是使用数组:

var pieces: [Piece?]

然后,我可以在pieces[25]中以恒定的时间访问某个位置的作品。我觉得这不太优雅,因为这个方法存储了每个Piece两次的位置:pieces数组中的位置以及rowcolumn的值变量

有没有办法通过哈希值(在恒定时间内)访问set元素?或者我应该只使用数组?

5 个答案:

答案 0 :(得分:2)

由于用于计算Set的属性可能会发生变化(因为用于计算hashValue的属性可能会发生变化),因此通常无法在hashValue Piece中获取Set元素。例如Set移动),hashValue不知道何时发生这种情况。如果[Position: Piece]能够以某种方式知道,那么它可能能够将元素存储在相对于WebElement mySelectElement = driver.findElement(By.id("mySelect")); Select dropdown= new Select(mySelectElement); 的新位置,以允许后续的常量查找。基本上,如果你将它们放在一个dropdown = new Select(driver.findElement(By.id("mySelect"))); 字典中,并为每次移动添加/删除键,你将会做什么。

在这种情况下,简单的数组可能是最好的解决方案。至于阵列浪费空间,它是space–time tradeoff的经典案例。

答案 1 :(得分:1)

序言:

  1. 由于有64个方块,可选件的数组可能是最好的
  2. 通常,您使用带有Int键的字典作为稀疏数组(请参阅下面的示例)
  3. 您不应该单独使用hashValue作为位置,因为它应该考虑所有属性(我在下面的示例中更改为位置) - 虽然我在这种情况下得到的除了瞬时没有2件可以具有相同的位置,因此它是好吧
  4. 我如何编码:

    struct Piece {
        var row: Int
        var column: Int
        let player: Player
        let type: PieceType
    
        var location: Int {
            return 8 * row + column
        }
    }
    
    struct Board {
        var pieces = [Int : Piece]()
    
        init(pieces: Piece...) {
            pieces.forEach {
                self.pieces[$0.location] = $0
            }
        }
    }
    
    let whiteKing = Piece(row: 0, column: 4, player: .White, type: .King)
    let blackKing = Piece(row: 7, column: 3, player: .Black, type: .King)
    
    let board = Board(pieces: whiteKing, blackKing)
    
    board.pieces // [4: {row 0, column 4, White, King}, 59: {row 7, column 3, Black, King}]
    

答案 2 :(得分:1)

为了使它更优雅,我会将下标添加到Board结构中。

struct Board {

  var grid: [Piece?]
  let rows: Int, columns: Int

  init(rows: Int, columns: Int) {
    self.rows = rows
    self.columns = columns
    grid = [Piece?](count: rows * columns, repeatedValue: nil)
  }

  func isValidIndex(row: Int, column: Int) -> Bool {
    return row >= 0 && row < rows && column >= 0 && column < columns
  }

  subscript(row: Int, column: Int) -> Piece? {
    get {
      assert(isValidIndex(row, column: column), "Index out of range")
      return grid[(row * columns) + column]
    }
    set(newValue) {
      assert(isValidIndex(row, column: column), "Index out of range")
      grid[(row * columns) + column] = newValue
    }
  }
}

获取/设置这样的部分:

var board = Board(rows: 8, columns: 8)
board[0, 1] = Piece(player: .White, type: .King)
board[0, 2] // nil

答案 3 :(得分:0)

我会坚持使用数组。

  

我觉得这不太优雅,因为这个方法存储每个Piece的位置两次:通过pieces数组中的位置以及行和列变量的值。

如果你可以通过哈希访问一个集合中的值(你不能这样做),你会遇到两次存储值相同的问题。并且数组将是访问元素的更好方式。

// array
pieces[25]

// set (not possible)
pieces.elementWithHash(25)

我实际上会将其更改为使用二维数组,因为它更具可读性。

var pieces: [[Piece?]]
pieces[3][1]

答案 4 :(得分:0)

或者你可以这样做:

$('.storage').on('change', function(e) {
//console.log(day_value + night_value);
  if(e.target && $(e.target).attr('id') == 'day_percent'){
    /* Write the logic for your calculation you want */
  }
  else
   if (typeof day_value != 'undefined' && typeof night_value != 'undefined')  {
  total = parseInt(day_value) + parseInt(night_value);
  console.log('total value: ' + total);
  day_percentage = (day_value / total) * 100;
  console.log('day value: ' + day_value);
  night_percentage = (night_value / total) * 100;
  console.log('night value: ' + night_value);

  $('#day_percent').val(day_percentage);
  $('#night_percent').val(night_percentage);

  }
});

您正在扫描8x8棋盘。不要再担心复杂性了。