Javascript - 尝试使用递归调用进行引用传递

时间:2018-03-03 14:23:56

标签: javascript recursion memcpy

在2人游戏的上下文中,我有一个执行递归调用的C函数,并且对于每个调用,它从全局数组变量和当前本地数组执行memcpy。这是C函数(“游戏”数组在函数外声明为全局变量):

int game[8][8];

int recursive_function(int player, int *mx, int *my, int depth)                           

{                                                                                        
  int x, y;
  int eval, e;                                                                           
  int mx2, my2;                                                                          
  int game2[8][8];

  // TERMINAL CASE
  if (depth == 0) {
    return 1;
  }

  memcpy(game2, game, sizeof(game));                                                     
  eval = -INFINITY;                                                                        

  for (x = 0; x < 8; x++)       
    for (y = 0; y < 8; y++) {
      if (isplayabe((x, y, player)) {                                                    
        e = -recursive_function(OTHER(player), &mx2, &my2, depth-1);                          
      }
      if (e > eval) {                                                                    
        *mx = x; *my = y;
        eval = e;
      }                                                                                  
      memcpy(game, game2, sizeof(game));                                                 
      }                                                                                  
  return eval;
}

现在,我想在javascript中实现相同的功能。

我想我必须使用对象进行引用传递。所以我为每个对象创建了对象HitHitTemp,将数组的坐标作为属性:

  1. Hit.coordPlaybles(x,y)坐标,并将该链接视为上面game变量的等效内容
  2. HitTemp.coordPlaybles(x,y)坐标,并将该链接视为上面game2变量的等效内容
  3. 从此,我尝试使用以下定义的对象Hit

    // Player white and black
    var playerWhite = ['white', 'black'];
    var playerBlack = ['black', 'white'];
    
    var Hit = {
     currentPlayer: playerBlack,
     coordPlayable: ['0', '0'],
     coordCurrent: ['0', '0'],
    };
    

    对象Hit在recursive_function(全局变量)之外声明,而HitTemp仅在此函数中定义(局部变量)。

    这是我做的:

    // Object Hit passed by reference
    function recursive_function(Hit, depth) {
      // Evaluation
      var eval, e;
    
       // TERMINAL CASE
          if (depth == 0) {
            return 1;
          }
    
      // Local Hit object : EQUIVALENT OF "memcpy" ??
      var HitTemp = Object.assign({}, Hit);
    
      eval = -infinity;       
    
      for (var x = 0; x < 8; x++)
        for (var y = 0; y < 8; y++) {
          if (isplayabe((x, y, HitTemp)) { 
            // CALL RECURSIVE WITH OPPOSITE PLAYER (SWITCHING PLAYER)                                                   
            e = -recursive_function(OTHER(HitTemp.currentPlayer), depth-1);  
            if (e > eval) {
                      // HitTemp.coordCurrent[0] = *mx
                      // HitTemp.coordPlayable[0] = x
                      // HitTemp.coordCurrent[1] = *my
                      // HitTemp.coordPlayable[1] = y
                      HitTemp.coordCurrent[0] = HitTemp.coordPlayable[0];
                      HitTemp.coordCurrent[1] = HitTemp.coordPlayable[1];
                      eval = e;
                    }
       // Final copy from HitTemp to Hitobject : Here also, equivalent to 
       // the final memcpy of C version ??
       Hit = Object.assign({}, HitTemp);
      }
    return eval;
    }
    

    不幸的是,两个版本之间的结果是完全不同的(我确信C版本工作正常)。

    有人能告诉我Javascript“assign”方法是否是重现C“memcpy”函数行为的正确方法?

    如果没有,你能给我一些线索来通过递归调用解决它吗?

    更新1:

    感谢您的帮助。但是,我提出了解决方案的问题 @Siltaar。实际上,如果我使用JSON.parse(JSON.stringify(HitCurrent))执行深度复制:

    // Global variables : Player white and black
    var playerWhite = ['white', 'black'];
    var playerBlack = ['black', 'white'];
    
    //Global variable Hit 
    var Hit = {
     currentPlayer: playerBlack,
     coordPlayable: ['0', '0'],
     coordCurrent: ['0', '0'],
    };
    
    function recursive_function(HitCurrent, depth) {
    
    // Deep copy
    var HitTemp = JSON.parse(JSON.stringify(HitCurrent));
    
    // Switch color for player
    
    // BEFORE
    console.log('BEFORE = ', HitTemp.currentPlayer);
    
    // Switch
    HitTemp.currentPlayer = (HitTemp.currentPlayer == playerBlack) ? playerWhite : playerBlack;
    
    // AFTER : expected the opposite of BEFORE
    console.log('AFTER = ', HitTemp.currentPlayer);
    
    ...
    }
    

    我得到了BEFOREAFTER

    BEFORE =  
    Array [ "black", "white" ]
    
    AFTER =  
    Array [ "black", "white" ]
    

    正如您所看到的,“HitTemp.currentPlayer”的值未切换,似乎“HitTemp.currentPlayer == playerBlack”设置为 false ,而“BEFORE”的值“HitTemp.currentPlayer”设置为“playerBlack”。

    我必须注意recursive_function中使用全局对象Hit调用main(,如:

        //Global variable Hit 
        var Hit = {
         currentPlayer: playerBlack,
         coordPlayable: ['0', '0'],
         coordCurrent: ['0', '0'],
        };
    
        // Main function
        function main() {
    
            recursive_function(Hit, maxDepth);
        ...
    
        }
    

    此问题是否与HitCurrentHitTemp对象的深层副本有关?

    更新2:

    如果我只使用一个“=”(而不是两个“==”):

    console.log('BEFORE = ', HitTemp.currentPlayer);
    HitTemp.currentPlayer = (HitTemp.currentPlayer = playerBlack) ? playerWhite : playerBlack;
    console.log('AFTER = ', HitTemp.currentPlayer);
    

    然后切换工作:什么是正确的语法(一个“=”符号或两个)与三元运算符的条件来测试相等?

2 个答案:

答案 0 :(得分:0)

Object.assign执行浅拷贝。根据您上面的评论,您似乎想要一份深层副本。如果你知道这个数据结构不会改变那么这样的东西就可以了:

var HitTemp = Object.assign({}, Hit, {
    coordPlayable: Hit.coordPlayable.slice(0),
    coordCurrent: Hit.coordCurrent.slice(0)
});

如果您想要更清洁,更通用的解决方案,请查看来自lodash的cloneDeep。由于这是游戏,性能可能是一个问题,因此question值得一试。

答案 1 :(得分:0)

MDN Object.assign() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

  

深度克隆警告

     

对于深度克隆,我们需要使用其他替代方法,因为   Object.assign()复制属性值。如果源值是a   引用一个对象,它只复制该引用值。

在您的情况下,currentPlayer: playerBlack未被深层复制。

解决方案如:

obj = JSON.parse(JSON.stringify(o));

此处已被告知:What is the most efficient way to deep clone an object in JavaScript?

关于 UPDATE 1 :您比较两个不同的对象,它们是具有相同值但内存中不同对象的副本。尽量只比较字符串或整数来区分你的玩家。 - Siltaar 2分钟前编辑

Regardine UPDATE 2 :只有一个等号“=”的语法是归因声明。归因通常会返回true。您正在尝试进行比较,因此我建议您坚持使用“==”或“===”(如果对象类型很重要)。