微软和OpenAI联手推出了GitHub Copilot这一AI编程工具,可根据开发者的输入和上下文,生成高质量的代码片段和建议

news2024/11/29 4:34:58

只需要写写注释,就能生成能够运行的代码?对于程序员群体来说,这绝对是一个提高生产力的超级工具,令人难以置信。实际上,早在2021年6月,微软和OpenAI联手推出了GitHub Copilot这一AI编程工具。它能够根据开发者的输入和上下文,生成高质量的代码片段和建议。这个工具看上去很好用很神奇,但我相信很多人仍然怀有一定的怀疑态度。让我们来亲身体验一下,看看效果如何。

首先我们需要访问 https://github.com/features/copilot/ Copilot的官方网站开通copilot的使用权限。

我们需要先登陆github的账户,然后点击开始免费试用后会跳转到开通确认页面,这里会提示我们开通后拥有60天的免费体验的时间。

虽然有免费体验的时间,但是我们还是要选择一个支付方案,因为试用期间不会真正扣费,所以我们大胆选择按月交费,每月10美元。点击获取访问Copilot功能按钮后就会进入下一步支付详情的页面,在这里我们要填上姓名、地址和国家。

填写完成就可以进入支付方式表单页面,我们按照页面要求正确填写好信用卡信息。

填写完成后,我们点击保存支付详情按钮,当网站的后台服务完成信用卡可用性验证后,页面会跳转到Copilot配置代码提示匹配范围的页面,这里我们需要选择是否匹配公共代码,如果选择Allow,Copilot就会在整个Github的公共代码库中进行匹配然后给出代码建议,否则的话它只会在当前Github账户的organizations中进行匹配。因为我们希望体验Copilot的神奇,所以这里我们选择Allow

当然,这个选项是可以更改的,我们可以到 https://github.com/settings/copilot 配置页面中进行更改。选择完成后,点击保存并开始使用,就会弹出支付成功的页面,页面上也提示我们可以去支持Copilot的IDE中安装插件了。

接下来我们在IDE中安装Copilot插件,这里我们使用的是VSCode,其它支持的IDE的安装方式大家可以在 https://docs.github.com/zh/copilot/configuring-github-copilot/configuring-github-copilot-in-your-environment 页面中找到,同时一些快捷键和使用技巧在官方文档中也都清晰的描述。

首先我们打开 VSCode 的扩展商店,搜索Copilot,会看到有许多相关的扩展,我们选择Github的官方扩展,注意不要选错了,然后点击安装。

安装完成后,右下角会出现处于加载状态的Copilot小图标,同时会提示我们需要先登录Github账号,才可以使用Copilot

按照指引完成登录以后,Copilot的小图标就能正常显示了。

接下来,就让我们体验一下Copilot的神奇之处,验证一下它生成的代码质量如何。这里要注意一点,Copilot是使用编程语言中的注释作为交互方式的,比如对于Javascript来说,输入//就可以激活Copilot,然后它会根据我们代码的上下文以及我们已经输入的部分注释内容,在代码库中匹配出合适的内容推荐出来,如果我们认为推荐的合适,可以按 tab 键应用推荐内容。然后按下回车开始进行下一行内容的提示。

斐波那契数列

作为一个经典的数学问题,Javascript实现起来也是有多种方式的,当我们注释中写完Fabonacci 数列求和函数,函数名是 fabonacci以后,一路按tab和回车,Copilot给我们生成了这样一份代码。

// Fabonacci 数列求和函数,函数名是 fabonacci
// 例如 fabonacci(10) // 55

function fabonacci(n) {
  if (n <= 1) {
    return n;
  }
  return fabonacci(n - 1) + fabonacci(n - 2);
}

console.log(fabonacci(10));

运行结果如下

当我写完第一行注释的描述后,它自动就推荐生成了第二行对于函数的进一步描述信息,而最终Copilot生成的代码也是按照这个注释中的描述实现的,可以看到结果完全没问题,代码的格式以及写法都算是很规范的。不过这个方法采用的是普通的递归方式实现,在数据量大的时候效率会降低,因此现在我们想用尾递归的方式实现这个方法,看看Copilot能不能帮我们写出正确的代码。

我们把注释信息修改为Fabonacci 数列求和函数,用 尾递归 方式实现,函数名是 fabonacci,还是一路tab加回车,基本没有手动输入什么代码,最后生成的代码如下:

// Fabonacci 数列求和函数,用 尾递归 方式实现,函数名是 fabonacci
// 用法:fabonacci(10) // 55

function fabonacci(n, a = 1, b = 1) {
  if (n <= 1) {
    return b;
  }
  return fabonacci(n - 1, b, a + b);
}

console.log(fabonacci(10));

运行结果如下

看到结果,我们会发现,第二次果然是用尾递归实现了一个Fabonacci求和方法,但是两次运行的结果不一样,而且第二次,运行的结果和注释中的要求也不太一样,难道是代码有错误?经过我们对代码的审查,发现这个尾递归代码中,参数变量a=1表示数列从1开始,而刚才的普通递归形式的代码中没有对数列的开始值进行处理,默认是从0开始的,所以两次同样是计算长度为10的数列合,结果却产生了分歧。我们将注释细化一下,要求数列的第一项为0 看看结果如何:

// Fabonacci 数列求和函数,用 尾递归 方式实现,函数名是 fabonacci
// 用法:fabonacci(10) // 55
// 数列要求从 0 开始

function fabonacci(n, a = 0, b = 1) {
  if (n <= 1) {
    return b;
  }
  return fabonacci(n - 1, b, a + b);
}

console.log(fabonacci(10));

运行结果如下

这回没问题了,两次运行的结果都是一致的了。

由此可见,如果我们在注释中没有非常明确的描述出必要的约束调教时,Capilot生成的代码可能会和我们的预期稍有不同。

贪吃蛇

刚刚我们使用Copilot解决了一个数学问题。这表明对于有标准算法思路的代码,它可以很好地推荐和补全代码。现在,我们要让Copilot帮我们制作一个小游戏,这种代码更偏向业务层面,需要更多的对代码上下文的理解。让我们看看这次Copilot的表现如何。

贪吃蛇这个小游戏相信大家都玩儿过,就是在一个n*m的区域中,我们使用键盘控制小蛇上下左右移动,吃到食物后小蛇长度加一;蛇头碰到自身或窗口边缘后,游戏失败。我们首先定义了一个Map类,用来构造游戏区域以及是否触及边界,绘制单元格等方法。

/**
 * ------------------------------------------------------------------
 * Map 类:用来构造游戏的区域范围,包括区域的行列等。
 * ------------------------------------------------------------------
 */
class Map {
  /**
   * 构造函数,初始化地图的行列等参数
   */
  constructor(col, row) {
    this.col = col || 24;
    this.row = row || 24;
    this.matrix = [];
    this.init();
  };
  /**
   * init() 方法:初始化地图
   */
  init() {
    if (this.row < 1 || this.col < 1) {
      throw new Error('Row and Column sizes must be at least 1');
    }
    this.matrix = [];
    for (let i = 0; i < this.row; i++) {
      this.matrix[i] = [];
      for (let j = 0; j < this.col; j++) {
        this.matrix[i][j] = 0;
      }
    }
  };
  /**
   * initMatrix() 方法:初始化地图的矩阵table,并添加到element元素上
   * @param:element 要添加到的元素
   */
  initMatrix(element) {
    // 基于 this.col和this.row 创建一个table,
    // 生成的table中每个cell存储到this.map的对应位置
    // 然后把table添加到element元素上
    let table = document.createElement("table");
    let tbody = document.createElement("tbody");
    for (let i = 0; i < this.col; i++) {
      let tr = document.createElement("tr");
      for (let j = 0; j < this.row; j++) {
        let td = document.createElement("td");
        this.matrix[i][j] = tr.appendChild(td);
      }
      tbody.appendChild(tr);
    }
    table.appendChild(tbody);
    element.appendChild(table);
  };
  
  /**
   * randomPoint(x, y) 方法:初始化地图的矩阵table,并添加到element元素上
   * @param:x 点的x坐标
   * @param:y 点的y坐标
   * @return:返回一个随机点
   * ------------------------------------------------------------------
   */
  randomPoint(x,y){
    let point = [];
    point[0] = Math.floor(Math.random()*x)+ 1;
    point[1] = Math.floor(Math.random()*y)+ 1;
    return point ;
  };

  /**
   * generateFood() 方法:根据地图的行列生成一个随机的食物
   * @return:食物的坐标
   * ------------------------------------------------------------------
   */
  generateFood() {
    const food = this.randomPoint(this.col, this.row);
    return food;
  };

  /**
   * drawCell(x, y, className): 根据坐标和类名,绘制一个cell
   * @param:x cell的x坐标
   * @param:y cell的y坐标
   * @param:className 要添加的类名
   */
  drawCell(x, y, className) {
    this.matrix[x][y].className = className;
  };

  /**
   * hitWall(point) 方法:判断是否撞墙
   * @param:point 点的坐标
   * @return:true or false
   */
  hitWall(point) {
    if(point instanceof Array){
      if(point[0]<0||point[0]>this.col-1||point[1]<0||point[1]>this.row-1){
        return true;
      }
    }
    return false;
  };
}

以及一个Snake类,用于描述小蛇的数据结构,判断是否触及小蛇身体等。

/**
 * ------------------------------------------------------------------
 * Snake 类:用来构造贪吃蛇,包括蛇的身体等。
 * ------------------------------------------------------------------
 */
class Snake {
  /**
   * 构造函数,初始化蛇的身体数组等参数
   */
  constructor() {
    this.body = [];
    this.init();
  };
  /**
   * init() 方法:初始化蛇的长度位置
   */
  init() {
    // 初始化蛇的位置
    this.body.push([1,3]);
    this.body.push([1,2]);
    this.body.push([1,1]);
  };
  /**
   * onSnake(point, includeHead = false) 方法:判断是否在蛇身上
   * @param:point 点的坐标
   * @param:includeHead 是否考虑作为脑袋的第一个cell
   * @return:true or false
   */
  onSnake(point, includeHead = false) {
    // 判断是否在蛇身上
    if (point instanceof Array) {
      for (var i = includeHead ? 0 : 1; i < this.body.length; i++) {
        if (point[0] == this.body[i][0] && point[1] == this.body[i][1]) {
          return true;
        }
      }
    }
    return false;
  }
}

上面两部分是我们准备的基础类,接下来就是Copilot大展身手的时候了,我们要实现一个Game类,用来实现游戏的初始化,小蛇的移动,游戏结果的判定等功能。按照Copilot的用法,我们先写注释,因为时业务逻辑,所以我们就尽量多的用注释将代码需要的业务逻辑描述清楚,看看结果如何。经过一系列的输入,tab和回车,Copilot帮我们生成了如下的代码:

/**
 * ------------------------------------------------------------------
 * Game 类:这款游戏的主类,包括了游戏的初始化,蛇的移动等。
 * ------------------------------------------------------------------
 */
class Game {
  /**
   * 构造函数,初始化游戏,包括地图,蛇,食物等
   */
  constructor() {
    // 蛇的实例
    this.snake = new Snake();
    // 地图的实例
    this.map = new Map();
    // 食物的坐标
    this.food = [];
    // 游戏的定时器
    this.timer = null;
    // 游戏是否停止
    this.stop = true;
    // 用于存储当前按下的按键
    this.pressedKey = 0;
    // 用于存储当前蛇的移动方向
    this.nextX = 0;
    this.nextY = 1;
    this.init();
  };
  /**
   * init() 方法:初始化游戏类
   */
  init() {
    // 初始化游戏
    // 生成地图
    this.map.initMatrix(document.body)
    // 将蛇绘制在屏幕上
    this.drawSnake();
    // 生成食物
    this.newFood();
    // 绑定bind方法,用来监听键盘的按下事件
    window.addEventListener("keydown", this.bind.bind(this));
  };
  /**
   * newFood():在地图上生成一个新的食物。
   * @returns 
   */
  newFood() {
    this.food = this.map.generateFood();
    // 调用snake实例的onSnake方法判断食物是否在蛇身上
    if (this.snake.onSnake(this.food, true)) {
      this.newFood();
      return false;
    }
    // 调用map实例的drawCell方法将食物绘制在屏幕上,类名为food
    this.map.drawCell(this.food[0],this.food[1], "food");
  };
  /**
   * drawSnake(): 调用 map 实例的drawCell接口将贪吃蛇绘制在屏幕上
   */
  drawSnake() {
    // 遍历snake实例的body数组,获取到每个cell的坐标,分别存储为_tempX和_tempY
    for(let i=0;i < this.snake.body.length; i++ ){
      let _tempX = this.snake.body[i][0], _tempY = this.snake.body[i][1];
      // 调用map实例的drawCell方法将_tempX和_tempY绘制在屏幕上,类名为snake
      this.map.drawCell(_tempX, _tempY, "snake");
    }
  };
  /**
   * bind(_e) 方法:用来监听键盘的按下事件 
   * @param _e 事件对象
   * @returns true or false
   */
  bind(_e) {
    // 将作为参数的事件对象赋值给e
    let e = _e;
    // 获取按下的键盘的键码
    let keycode = e ? e.keyCode : 0;
    // 判断按下的键码,如果是空格键,则暂停或继续游戏,如果是上下左右键,将keyCode赋值给pressedKey
    switch (keycode) {
      case 32:
        // 如果 this.stop 被设置成 true 了,就继续游戏,否则清除定时器,暂停游戏
        if (this.stop) {
          this.timer = setInterval(this.move, 200);
        } else {
          clearInterval(this.timer);
        }
        this.stop = !this.stop;
        break;
      // 上下左右键
      case 37:
      case 38:
      case 39:
      case 40:
        // 如果游戏不是暂停状态,将keyCode赋值给pressedKey
        if (!this.stop) {
          this.pressedKey = keycode;
        }
        break;
    }
    return false;
  };

  /**
   * start() 方法:游戏的主要逻辑
   */
  start() {
    // 游戏的主要逻辑
    // 首先定义蛇头所在的位置
    let x = this.snake.body[0][0];
    let y = this.snake.body[0][1];
    // 定义蛇尾所在的位置
    let tail = this.snake.body[this.snake.body.length - 1];
    // 定义游戏是否结束的标志
    let over = false;

    // 根据按下的按键来改变蛇的移动方向
    switch (this.pressedKey) {
      // 向左
      case 37:
        this.nextX = 0;
        this.nextY = -1;
        break;
      // 向上
      case 38:
        this.nextX = -1;
        this.nextY = 0;
        break;
      // 向右
      case 39:
        this.nextX = 0;
        this.nextY = 1;
        break;
      // 向下
      case 40:
        this.nextX = 1;
        this.nextY = 0;
        break;
    }
    // 根据蛇的移动方向来改变蛇头的位置
    x += this.nextX;
    y += this.nextY;
    // 判断是否吃到食物
    if (x === this.food[0] && y === this.food[1]) {
      // 如果吃到食物,将食物的位置添加到蛇的body数组中第一项作为新的蛇头
      this.snake.body.unshift([x, y]);
      // 生成新的食物
      this.newFood();
    } else {
      // 如果没有吃到食物,判断蛇是否撞到了自己或者撞到了墙
      // 先调用 map 实例的 hitWall 方法判断蛇头是否撞到了墙
      if (this.map.hitWall(x, y)) {
        over = true;
      } else if (this.snake.onSnake([x, y], false)) {
        // 再调用 snake 实例的 onSnake 方法判断蛇头是否撞到了自己
        over = true;
      }
      // 如果撞到墙或者自己了,设置游戏结束的标志为 true
      if (over) {
        // 清除定时器
        clearInterval(this.timer);
        // 弹出游戏结束的提示
        alert("游戏结束");
        return false;
      }
    }
  };
  /**
   * move() 贪吃蛇移动
   */
  move() {
    // 蛇移动
    // 使用定时器调用start方法,每隔200毫秒调用一次
    this.timer = setInterval(this.start.bind(this), 200);

  };
}
// 实例化一个Game对象
new Game();

我们将文件保存为snake.js,然后创建一个html文件用来作为这个小游戏的页面文件,代码如下:

<!DOCTYPE>
<html>
<head>
  <title>贪吃蛇</title>
  <head>
    <style>
      * { margin: 0; padding: 0; border: 0px; }
      body { background-color: #37393b; }
      table { border-collapse: collapse; overflow: hidden; border: 1px solid #d0e3f2; margin: 8px auto; }
      td { display: table-cell; width: 16px; height: 16px; background: #87a4a9; border: 1px #bccfdf solid; }
      #info { width: 450px; margin: 24px auto; text-align: center; font-size: 14px; color: #fff;}
      .snake { background: #42e5d4; }
      .empty { background: #87a4a9; }
      .food { background: #f1ec5a; }
    </style>
  </head>
</head>
<body>
  <div>
    <p id="info">空格键控制开始,方向键控制小蛇的移动方向</p>
  </div>
  <script type="text/javascript" src="snake.js"></script>
</body>
</html>

在浏览器中加载后,我们看到游戏界面可以正常加载出来,那关键能不能运行呢? 我们按下空格键,却返现小蛇并没有移动,打开浏览器的控制台,看到有如下报错:

Copilot在实现绑定上下文指针this的代码时,使用了某代码片段中的bind方法,但是这个方法并不是Javascript解释器的方法,而我们也没有定义这个方法,所以出错了。知道原因了,我们就修复一下,使用Javascript标准的绑定上下文方法修改一下,而且有两处地方使用了bind,所以我们修改如下:

class Game{

  /**
   * init() 方法:初始化游戏类
   */
  init() {
    // 初始化游戏
    // 生成地图
    this.map.initMatrix(document.body)
    // 将蛇绘制在屏幕上
    this.drawSnake();
    // 生成食物
    this.newFood();
    // 绑定bindKeydown方法,用来监听键盘的按下事件
    let _this = this;
    document.onkeydown = _this.bindContext(_this.bindKeydown, _this);
  };

  ...

  /**
   * bindContext(fn,context) 方法:用来继承运行上下文
   * @param fn 需要继承的方法
   * @param context 
   * @returns 继承以后的方法
   */
  bindContext(fn,context) {
			return function(){
				return fn.apply(context,arguments);
			}
  };

  ...

  /**
   * move() 贪吃蛇移动
   */
  move() {
    // 蛇移动
    // 使用定时器调用start方法,每隔200毫秒调用一次
    // 使用本类中的 bindContext 方法继承 this
    let _this = this;
    // 先清除定时器
    if (_this.timer) {
      clearInterval(_this.timer);
    }
    // 再重新设置定时器
    _this.timer = setInterval(_this.bindContext(_this.start, _this), 200);

  };
}

改好以后运行,发现还是有问题,会提示 move 方法中的_this.bindContext 不存在:

这就比较奇怪了,因为已经在方法中绑定了运行时的this上下文,说明有调用move的地方没有继承运行上下文指针this,去代码中找了一下,果然有一处异步调用没有传递上下文指针,因为这里其实不用延时调用,我们把这里改成直接调用就可以:

  /**
   * bindKeydown(_e) 方法:用来监听键盘的按下事件 
   * @param _e 事件对象
   * @returns true or false
   */
  bindKeydown(_e) {
    // 将作为参数的事件对象赋值给e
    let e = _e;
    // 获取按下的键盘的键码
    let keycode = e ? e.keyCode : 0;
    // 判断按下的键码,如果是空格键,则暂停或继续游戏,如果是上下左右键,将keyCode赋值给pressedKey
    switch (keycode) {
      case 32:
        // 如果 this.stop 被设置成 true 了,就继续游戏,否则清除定时器,暂停游戏
        if (this.stop) {
          this.move();
        } else {
          clearInterval(this.timer);
        }
        this.stop = !this.stop;
        break;
      // 上下左右键
      case 37:
      case 38:
      case 39:
      case 40:
        // 如果游戏不是暂停状态,将keyCode赋值给pressedKey
        if (!this.stop) {
          this.pressedKey = keycode;
        }
        break;
    }
    return false;
  };

改好以后运行,这回我们的小蛇可以成功动起来,并且可以吃到食物啦,不过当我们完了一下以后,发现小蛇在撞墙的时候并没有提示结束游戏,反而报错了,报错如下:

经过检查,我们发现时调用是否碰撞到墙壁的方法时参数传递的问题,并没有按照方法的参数要求将坐标作为一个参数以数组的形式传递到方法中,于是我们进行了修改:

  ...

  } else {
    // 如果没有吃到食物,判断蛇是否撞到了自己或者撞到了墙
    // 先调用 map 实例的 hitWall 方法判断蛇头是否撞到了墙
    if (this.map.hitWall([x, y])) {
    over = true;
    } else if (this.snake.onSnake([x, y], false)) {
    // 再调用 snake 实例的 onSnake 方法判断蛇头是否撞到了自己
    over = true;
    }
    // 如果撞到墙或者自己了,设置游戏结束的标志为 true
    if (over) {
  ...

这回,我们的小蛇可以正常吃到食物,撞墙也能正确提示游戏结束了,但是还是发现一个问题,当我们控制方向键的时候,如果按下和小蛇行进方向的反方向按键的时候,也提示游戏结束,这是不对的,我们需要修改一下,当按下和小蛇行进方向相反的按键时,对于下一个点的坐标不做任何改变,修改如下:

    ...
    // 定义游戏是否结束的标志
    let over = false;

    // 根据按下的按键来改变蛇的移动方向
    switch (this.pressedKey) {
      // 向左
      case 37:
        if (this.nextY != 1) { this.nextX = 0; this.nextY = -1; }
        break;
      // 向上
      case 38:
        if (this.nextX != 1) { this.nextX = -1; this.nextY = 0; }
        break;
      // 向右
      case 39:
        if (this.nextY != -1) { this.nextX = 0; this.nextY = 1; }
        break;
      // 向下
      case 40:
        if (this.nextX != -1) { this.nextX = 1; this.nextY = 0; }
        break;
    }
    ...

这样,我们的小游戏就可以比较完美的运行了。

通过制作这个小游戏,我们可以看出Copilot在程序开发方面非常友好。它不仅能够生成正确的代码,还能理解开发者的意图和上下文,生成符合实际需求的代码片段。但是在某些特殊场景下,我们仍然需要对生成的代码进行调整,特别是在编写涉及敏感信息和安全问题的代码时,我们需要更加谨慎,避免泄露机密信息。此外,我们还应该遵守版权和知识产权相关规定,以确保不侵犯他人的权利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/550453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机网络复习】第四章 网络层 3

路由器的功能和层次 o 计算机网络的核心设备 o 具有多个输入接口和多个输出接口 o 任务是转发IP包&#xff1a;将从某个输入接口收到的I包&#xff0c;按照要去的目的地&#xff08;即目的网络&#xff09;&#xff0c;从路由器的某个合适的输出接口转发给下一跳路由器 …

基于ssm+vue的驾校在线培训平台

基于ssmvue的驾校在线培训平台 系统功能 普通用户 新闻咨讯&#xff1a;可以查看系统新闻并进行评论、收藏和点赞 教资信息查看&#xff1a;普通用户登录系统可以查看驾校教资情况 系统通知信息&#xff1a;用户可以查看网站相关通知公告信息 在线报名&#xff1a;普通用户可…

C++ 初始模板

模板 void Swap(int* x, int* y) {int tmp *x;*x *y;*y tmp; }void Swap(double* x, double* y) {double tmp *x;*x *y;*y tmp; }void Swap(char* x, char* y) {char tmp *x;*x *y;*y tmp; } 如上述所示&#xff0c;我们在实现函数的时候&#xff0c;有很多函数会像…

【C++ 入坑指南】(09)数组

文章目录 简介一维数组1. 定义2. 特点3. 用途4. 示例 二维数组1. 定义2. 用途3. 示例 简介 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 一维数组 1. 定义…

外企还是香啊~

小伙伴们大家好&#xff0c;我是阿秀。 三月份的时候我看了下外企&#xff0c;查了一些资料&#xff0c;最后查下来远远比我想的要多&#xff0c;可能很多人跟我一样&#xff0c;对外企的印象都停留在微软、谷歌、intel这些比较市值大的公司上。 其实远远不止&#xff0c;广义上…

密码学安全性证明(一)Cramer-Shoup密码系统

Cramer-Shoup密码系统来自于A Practical Public Key CryptosystemProvably Secure against Adaptive ChosenCiphertext Attack这篇论文 CDH问题回顾&#xff1a; 已知(g,g^x, gk)能否计算gxk DDH问题回顾&#xff1a; 已知(g,g^x, g^k &#xff0c;D)能否判断D是否等于g^xk 注意…

港科夜闻|香港科技大学赛马会研究院两位成员入选美国国家科学院

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科技大学赛马会研究院两位成员入选美国国家科学院。香港科技大学赛马会研究院Gunther Uhlmann教授和香港科技大学客座教授戴碧瓘教授因在原创性研究方面的杰出和可持续的成就&#xff0c;入选美国国家科学院(NAS)。G…

API接口的工作原理以及可以帮我们实现什么功能?

一、API接口的工作原理 API接口是应用程序编程接口(Application Programming Interface)的缩写&#xff0c;是不同软件系统之间进行通信的一种方式。 API接口的工作原理是&#xff0c;通过预定义的接口规范&#xff0c;软件系统可以调用或提供API接口的服务&#xff0c;来实现…

【开源项目】权限框架Nepxion Permission原理解析

项目介绍 Nepxion Permission是一款基于Spring Cloud的微服务API权限框架&#xff0c;并通过Redis分布式缓存进行权限缓存。它采用Nepxion Matrix AOP框架进行切面实现&#xff0c;支持注解调用方式&#xff0c;也支持Rest调用方式 项目地址 https://toscode.gitee.com/nepxion…

实现图形算法API[软光栅渲染器,C++]

最近有点烦&#xff0c;发烧感冒了三天[事实上是俩天&#xff0c;第三天是因为摆得太舒服了索性多玩一天]&#xff0c;啥都没学&#xff0c;打守望先锋也把把被虐...&#xff0c;想着今天来提起键盘把之前的东西都总结一下。 那么话归真题&#xff0c;首先我是仿造opengl来写的…

tensorflow/keras如何自定义layer

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【Linux高级 I/O(3)】如何使用阻塞 I/O 与非阻塞 I/O?——poll()函数

poll()函数介绍 系统调用 poll()与 select()函数很相似&#xff0c;但函数接口有所不同。在 select()函数中&#xff0c;我们提供三个 fd_set 集合&#xff0c;在每个集合中添加我们关心的文件描述符&#xff1b;而在 poll()函数中&#xff0c;则需要构造一个 struct pollfd 类…

opencv图像增强实现方法

opencv是一款开源的图像增强工具&#xff0c;主要用于在 python环境下实现图像增强功能。 使用 opencv实现图像增强&#xff0c;需要使用 opencv的 GUI模块&#xff0c;如图1所示。 在 opencv中&#xff0c;有一个 datasets模块&#xff0c;这个模块主要用于处理数据和可视化操…

剑指 Offer 30. 包含min函数的栈【辅助栈】

剑指 Offer 30. 包含min函数的栈【辅助栈】 文章目录 剑指 Offer 30. 包含min函数的栈【辅助栈】题目描述题解 题目描述 题解 class MinStack {/*** initialize your data structure here.*/Stack<Integer> A, B;public MinStack(){A new Stack<Integer>();B ne…

使用OpenCV进行肺炎诊断检测

肺炎是一种由感染引起的严重呼吸道疾病&#xff0c;特别是在高危人群中&#xff0c;可能会出现危及生命的并发症。必须尽快诊断和治疗肺炎&#xff0c;以最大限度地提高患者康复的机会。 诊断过程并不容易&#xff0c;需要一些医学实验室工具和先进的医疗技能&#xff0c;但我们…

计算机专业毕业,有人Offer 50w,有人挂科重修!

昨天有两个VIP的小伙伴问我问题&#xff1a; 同学小明&#xff1a;孟哥&#xff0c;我小硕一枚&#xff0c;有两个offer&#xff0c;一个拿到了阿里的offer&#xff0c;乱七八糟加起来有四五十&#xff1b;还有一个是老家的电网。但是父母想让我回去&#xff0c;毕竟稳定&#…

Kali-linux绕过Utilman登录

Utilman是Windows辅助工具管理器。该程序是存放在Windows系统文件中最重要的文件&#xff0c;通常情况下是在安装系统过程中自动创建的&#xff0c;对于系统正常运行来说至关重要。在Windows下&#xff0c;使用WindowsU组合键可以调用Utilman进程。本节将介绍绕过Utilman程序登…

瑞芯微RGMII的配置

主要配置项 除去复位等信号&#xff0c;我们主要关注两大块的配置&#xff1a; 时钟配置 MAC 采用125M时钟&#xff0c;PHY采用25M时钟。 主要配置时钟源&#xff0c;这个和具体硬件连线强相关。例如125M时钟可以来源于soc内部的PLL&#xff0c;也可以由对端PHY 提供。 由对端P…

【JS】1688- 重学 JavaScript API - Fetch API

❝ 前期回顾&#xff1a; 1. Page Visibility API 2. Broadcast Channel API 3. Beacon API 4. Resize Observer API 5. Clipboard API ❞ &#x1f3dd; 1. 什么是 Fetch API 1.1 概念介绍 Fetch API[1] 是一种现代的 JavaScript API&#xff0c;用于进行「网络请求」。它提供…

过滤器(filter)、watch 侦听器 、计算属性 、axios、vue-cli 的使用 、vue组件化

过滤器&#xff08;filter&#xff09;、watch 侦听器 、计算属性 、axios、vue-cli 的使用 、vue组件化 1.过滤器&#xff08;filter&#xff09;过滤器的注意点定义全局变量 2.watch 侦听器侦听器的格式 3.计算属性4.axiosaxios 的基本使用 5.vue-cli 的使用6.vue组件化 1.过…