草庐IT

javascript - Diamond-Square 实现产生的值(value)太高

coder 2024-05-14 原文

我已经实现了一个生成高度图的 Diamond-Square 函数。乍一看,该实现似乎工作正常。

这只是两个示例,但已经可以看出输出值总体上似乎相当高。只有很少的真正黑暗的值(value)观。一世。 E. 如果您查看此 paper 中的高度图(由 diamond square 制作)你可以看到它们不像我的那么均匀。不同地区之间有更多的偏移量。有些区域看起来像陨石坑。

我无法弄清楚此行为的原因是错误的参数化还是实现。尽管 Web 上的示例实现确实略有不同,但我认为我已经掌握了基本思路。

我正在处理平面类型数组。我传递给函数的参数是:

  • 边长
    • 因为我有一个表示二维矩阵的平面数组,所以我传递网格边长以进行进一步计算。我在这里传递了 257 的值。
  • 最大高度
    • 可能的最高输出值。我在这里传递了 255,因为我稍后会使用输出来在 Canvas 上渲染高度图。
  • 粗糙度
    • 这是我在方形步长中使用的偏移值,目的是产生更多的随机高度偏移。这里我通常取50左右的值。

我正在调用 Heightmap 函数以获得输出:

/**
 * Creates a heightmap based on parameters passed.
 * @param {number} sideLength - Side length of a the resulting grid array. Diamond-Square can only have a size (2^n)+1.
 * @param {number} maxHeight - Max height value for the heightmap's values.
 * @param {number} roughness - A factor which is used as offset value for the heightmap. Defines the roughness of a heightmap.
 * @returns {Float32Array} - A flat `Float32Array` representing a 2D-grid with size `sideLength * sideLength`.
 */
static HeightMap(sideLength, maxHeight, roughness) {

    const n = Math.log(sideLength - 1) / Math.log(2);
    if (n < 0 || n % 1 != 0) {
        throw "Invalid side length in Diamond Square: Side Length has to be in range of `(2^n) + 1`.";
    }

    let gridArray = new Float32Array(sideLength * sideLength);
    this._initGrid(gridArray, sideLength, maxHeight);
    this._seed(gridArray, sideLength, roughness);

    return gridArray;
}

这里首先启动“网格”:

/**
 * Sets the initial corner values for a Diamond-Square grid.
 * @param {Float32Array} gridArray - An `Float32Array` with its values (ideally) set to `0`.
 * @param {number} sideLength - Side length of a the resulting grid array. Diamond-Square can only have a size `(2^n)+1`.
 * @param {number} maxHeight - Max height value for the heightmap's values.
 * @returns {Float32Array} - A flat `Float32Array` representing a 2D-grid with its NW, NE, SE and SW values initialized.
 */
static _initGrid(gridArray, sideLength, maxHeight) {

    gridArray[0] = MathHelper.RandomInt(0, maxHeight); // NW
    gridArray[sideLength - 1] = MathHelper.RandomInt(0, maxHeight); // NE
    gridArray[sideLength * sideLength - 1] = MathHelper.RandomInt(0, maxHeight); // SE
    gridArray[sideLength * sideLength - sideLength] = MathHelper.RandomInt(0, maxHeight); // SW

    return gridArray;
}

之后 HeightMap 函数调用 _seed,这基本上是 Diamond-Square 循环:

/**
 * Performs the Diamond Square (aka. Midpoint displacement) algorithm on a given flat TypedArray.
 * @param {Float32Array} gridArray - An (Diamond-Square-initialized) `Float32Array`.
 * @param {number} sideLength - Side length of a the resulting grid array.
 * @param {number} roughness - A factor which is used as offset value for the heightmap. Defines the roughness of a heightmap.
 * @returns {Float32Array} - Returns a ready to use heightmap produced by the Diamond-Square algorithm.
 */
static _seed(gridArray, sideLength, roughness) {
    let step = Math.sqrt(gridArray.length) - 1;
    let size = Math.sqrt(gridArray.length) - 1;
    let currentRoughness = roughness;

    while (step / 2 >= 1) {

        let numSquares = (Math.pow(size, 2)) / (Math.pow(step, 2));
        let perRowSquares = Math.floor(Math.sqrt(numSquares));
        for (let i = 0; i < perRowSquares; i++) {
            for (let j = 0; j < perRowSquares; j++) {
                const nwIndex = this._getNWIndex(i, j, step, sideLength);
                const cornerValues = this._getCornerValues(nwIndex, gridArray, sideLength, step);
                this._diamondStep(nwIndex, cornerValues, gridArray, sideLength, step, currentRoughness);
                this._squareStep(nwIndex, cornerValues, gridArray, sideLength, step, currentRoughness);
            }
        }

        currentRoughness /= 2.0;
        step /= 2;
    }

    return gridArray;
}

请注意,我正在根据当前西北指数的指数计算位置指数。为此,我有一个功能:

/**
 * Returns the array index for the north-west value for the current step.
 * @param {number} i - Current row, I guess.
 * @param {number} j - Current column, I guess.
 * @param {number} stepSize - Current step size.
 * @param {number} sideLength - Grid's side length.  
 * @returns {number} - Returns the index for current north-west value.
 */
static _getNWIndex(i, j, stepSize, sideLength) {
    return (i * (stepSize * sideLength)) + j * stepSize;
}

因为所有四个 Angular 值都用在菱形和方形步骤中,所以我也有一个函数:

/**
 * Return an array holding the north-west, north-east, south-west and south-east values for the current step.
 * @param {number} nwIndex - North-West index for current step. 
 * @param {Float32Array} gridArray - The corner values for the current step.  
 * @param {number} sideLength - Grid's side length. 
 * @param {number} stepSize - Current step size.  
 * @returns {Float32Array} - Returns the typed array the function of operating on.
 */
static _getCornerValues(nwIndex, gridArray, sideLength, stepSize) {
    return [
        gridArray[nwIndex], // NW
        gridArray[nwIndex + stepSize], // NE
        gridArray[nwIndex + stepSize * sideLength], // SW
        gridArray[nwIndex + stepSize + stepSize * sideLength] // SE
    ];
}

最后但同样重要的是,我有 _diamondStep_sqaureStep:

/**
 * Performs the Diamond Step by setting the center value for the current step.
 * @param {number} nwIndex - North-West index for current step.
 * @param {number[]} cornerValues - The corner values for the current step.
 * @param {Float32Array} gridArray - Array holding heightmap data. Function will write to this array.
 * @param {number} sideLength - Grid's side length. 
 * @param {number} stepSize - Current step size.
 * @returns {Float32Array} - Returns the typed array the function of operating on.
 */
static _diamondStep(nwIndex, cornerValues, gridArray, sideLength, stepSize, roughness) {

    // Center point. Calculated from "East - `stepSize / 2`"
    gridArray[(((nwIndex + stepSize * sideLength) + stepSize) - (stepSize * sideLength) / 2) - stepSize / 2]
        = (cornerValues[0] + cornerValues[1] + cornerValues[2] + cornerValues[3]) / 4 + (roughness * MathHelper.RandomInt(-1, 1));

    return gridArray;
}

/**
 * Performs the Square Step by setting the north, east, south and west values for the current step.
 * @param {number} nwIndex - North-West index for current step.
 * @param {number[]} cornerValues - The corner values for the current step. 
 * @param {Float32Array} gridArray - Array holding heightmap data. Function will write to this array. 
 * @param {number} sideLength - Grid's side length.  
 * @param {number} stepSize - Current step size. 
 * @param {number} roughness - Roughness factor for the current step.
 * @returns {Float32Array} - Returns the typed array the function of operating on.
 */
static _squareStep(nwIndex, cornerValues, gridArray, sideLength, stepSize, roughness) {

    const average = (cornerValues[0] + cornerValues[1] + cornerValues[2] + cornerValues[3]) / 4;
    const value = average + (roughness * MathHelper.RandomInt(-1, 1));

    // N
    gridArray[nwIndex + (stepSize / 2)] = value;
    // E
    gridArray[((nwIndex + stepSize * sideLength) + stepSize) - (stepSize * sideLength) / 2] = value;
    // S
    gridArray[(nwIndex + stepSize * sideLength) + stepSize / 2] = value;
    // W
    gridArray[(nwIndex + stepSize * sideLength) - (stepSize * sideLength) / 2] = value;

    return gridArray;
}

正如我之前提到的,该实现似乎有效。我仍然想知道整体“白度”是否是由错误的参数化或错误的实现引起的?

这是一个有效的 fiddle :

function HeightMap(sideLength, maxHeight, roughness) {

  const n = Math.log(sideLength - 1) / Math.log(2);
  if (n < 0 || n % 1 != 0) {
    throw "Invalid side length in Diamond Square: Side Length has to be in range of `(2^n) + 1`.";
  }

  let gridArray = new Float32Array(sideLength * sideLength);
  _initGrid(gridArray, sideLength, maxHeight);
  _seed(gridArray, sideLength, roughness);

  return gridArray;
}


function _initGrid(gridArray, sideLength, maxHeight) {

  gridArray[0] = RandomInt(0, maxHeight); // NW
  gridArray[sideLength - 1] = RandomInt(0, maxHeight); // NE
  gridArray[sideLength * sideLength - 1] = RandomInt(0, maxHeight); // SE
  gridArray[sideLength * sideLength - sideLength] = RandomInt(0, maxHeight); // SW

  return gridArray;
}


function _seed(gridArray, sideLength, roughness) {
  let step = Math.sqrt(gridArray.length) - 1;
  let size = Math.sqrt(gridArray.length) - 1;
  let currentRoughness = roughness;

  while (step / 2 >= 1) {

    let numSquares = (Math.pow(size, 2)) / (Math.pow(step, 2));
    let perRowSquares = Math.floor(Math.sqrt(numSquares));
    for (let i = 0; i < perRowSquares; i++) {
      for (let j = 0; j < perRowSquares; j++) {
        const nwIndex = _getNWIndex(i, j, step, sideLength);
        const cornerValues = _getCornerValues(nwIndex, gridArray, sideLength, step);
        _diamondStep(nwIndex, cornerValues, gridArray, sideLength, step, currentRoughness);
        _squareStep(nwIndex, cornerValues, gridArray, sideLength, step, currentRoughness);
      }
    }

    currentRoughness /= 2.0;
    step /= 2;
  }

  return gridArray;
}


function _diamondStep(nwIndex, cornerValues, gridArray, sideLength, stepSize, roughness) {
  gridArray[(((nwIndex + stepSize * sideLength) + stepSize) - (stepSize * sideLength) / 2) - stepSize / 2] =
    (cornerValues[0] + cornerValues[1] + cornerValues[2] + cornerValues[3]) / 4 + (roughness * RandomInt(-1, 1));

  return gridArray;
}

function _squareStep(nwIndex, cornerValues, gridArray, sideLength, stepSize, roughness) {

  const average = (cornerValues[0] + cornerValues[1] + cornerValues[2] + cornerValues[3]) / 4;
  const value = average + (roughness * RandomInt(-1, 1));

  // N
  gridArray[nwIndex + (stepSize / 2)] = value;
  // E
  gridArray[((nwIndex + stepSize * sideLength) + stepSize) - (stepSize * sideLength) / 2] = value;
  // S
  gridArray[(nwIndex + stepSize * sideLength) + stepSize / 2] = value;
  // W
  gridArray[(nwIndex + stepSize * sideLength) - (stepSize * sideLength) / 2] = value;

  return gridArray;
}

function _getCornerValues(nwIndex, gridArray, sideLength, stepSize) {
  return [
    gridArray[nwIndex], // NW
    gridArray[nwIndex + stepSize], // NE
    gridArray[nwIndex + stepSize * sideLength], // SW
    gridArray[nwIndex + stepSize + stepSize * sideLength] // SE
  ];
}

function _getNWIndex(i, j, stepSize, sideLength) {
  return (i * (stepSize * sideLength)) + j * stepSize;
}

function GenerateIterations(max) {
  let iterations = [];
  for (let n = 0; n < max; n++) {
    iterations.push(Math.pow(2, n) + 1);
  }
  return iterations;
}

function Grayscale(canvasName, data, rows, cols) {
  let canvas = document.getElementById(canvasName);
  let ctx = canvas.getContext("2d");

  let imageData = ctx.createImageData(cols, rows);

  for (let i = 0; i < data.length; i++) {
    const color = data[i];
    imageData.data[i * 4] = color;
    imageData.data[i * 4 + 1] = color;
    imageData.data[i * 4 + 2] = color;
    imageData.data[i * 4 + 3] = 255;
  }

  ctx.putImageData(imageData, 0, 0);
}

function RandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

let terrainGrid = HeightMap(257, 255, 50);
Grayscale('grayscaleCanvas', terrainGrid, 257, 257);
.greyscaleCanvas {
  border: solid 1px black;
}
<canvas id="grayscaleCanvas" class="greyscaleCanvas" width="257px" height="257px"></canvas>

最佳答案

因此,我根据我对该算法工作原理的理解对代码进行了一些修改。这完全有可能仍然无法正常工作,但我认为不管怎样,这段代码都会让您更容易使用。

根据我在维基百科上阅读的菱形方形步骤的理解,我想我也发现了您的算法中的一些问题:

function HeightMap(sideLength, maxHeight, roughness) {

  const n = Math.log(sideLength - 1) / Math.log(2);
  if (n < 0 || n % 1 != 0) {
    throw "Invalid side length in Diamond Square: Side Length has to be in range of `(2^n) + 1`.";
  }

  let gridArray = new Array(sideLength);
  for (var i = 0; i < gridArray.length; i++) {
    gridArray[i] = new Float32Array(sideLength);
    }
  gridArray = _initGrid(gridArray, sideLength, maxHeight);
    gridArray = _seed(gridArray, sideLength, roughness);
  return gridArray;
}


function _initGrid(gridArray, sideLength, maxHeight) {

  gridArray[0][0] = RandomInt(0, maxHeight); // NW
  gridArray[0][sideLength-1] = RandomInt(0, maxHeight); // NE
  gridArray[sideLength-1][sideLength-1] = RandomInt(0, maxHeight); // SE
  gridArray[sideLength-1][0] = RandomInt(0, maxHeight); // SW

  return gridArray;
}


function _seed(gridArray, sideLength, roughness) {
  let step = sideLength - 1;
  let size = sideLength - 1;
  let currentRoughness = roughness;

    let run_num = 0
  while (step / 2 >= 1) {
    console.log(run_num)
        run_num = run_num + 1
    let numSquares = Math.pow(size, 2) / Math.pow(step, 2);
    let perRowSquares = Math.floor(Math.sqrt(numSquares));
    for (let i = 0; i < perRowSquares; i++) {
      for (let j = 0; j < perRowSquares; j++) {
        row = i*step
        col = j*step
        const squareCornerValues = _getSquareCornerValues(gridArray, row, col, step)
        gridArray = _diamondStep(squareCornerValues, row, col, step, gridArray, currentRoughness);
        gridArray = _squareStep(row, col, step, gridArray, sideLength, currentRoughness)
       // _squareStep(diamondMidPoints, gridArray, step, currentRoughness);
      }
    }

    currentRoughness /= 2.0;
    step /= 2;
  }
  return gridArray;
}


function _diamondStep(squareCornerValues, i, j, step, gridArray, currentRoughness) {
  gridArray[row+step/2][col+step/2] = (squareCornerValues[0] + squareCornerValues[1] + squareCornerValues[2] + squareCornerValues[3] ) / 4  + (currentRoughness * RandomInt(-1, 1));
  return gridArray;
}

function _squareStep(row, col, step, gridArray, sideLength, currentRoughness) {

  let diamondMidPoints = [[row, col+step/2], //top
                                                [row+step/2, col], //left
                            [row+step, col+step/2], //right
                            [row+step/2, col+step] //bottom
                             ];
  for (let z = 0; z < diamondMidPoints.length; z++){
    corners = _get_diamond_corners(diamondMidPoints[z], step, sideLength, gridArray);
    gridArray[diamondMidPoints[z][0]][diamondMidPoints[z][1]] = (corners[0] + corners[1] + corners[2] + corners[3]) /4  + (currentRoughness * RandomInt(-1, 1));
  }

  return gridArray;
}

function _getSquareCornerValues(gridArray, row, col, step) {
  return [
    gridArray[row][col], // NW
    gridArray[row][col+step], // NE
    gridArray[row+step][col], // SW
    gridArray[row+step][col+step] // SE
  ];
}

function _get_diamond_corners(diamondMidPoints, step, sideLength, gridArray){
    row = diamondMidPoints[0];
  col = diamondMidPoints[1];
  top_coord = [(row  - step/2 + sideLength) % sideLength, col];
  left_coord = [row, (col - step/2 + sideLength) % sideLength];
  right_coord = [row, (col + step/2 + sideLength) % sideLength];
  bottom_coord = [(row + step/2 + sideLength) % sideLength, col];
  return [gridArray[top_coord[0]][top_coord[1]],
                gridArray[left_coord[0]][left_coord[1]],
          gridArray[right_coord[0]][right_coord[1]],
          gridArray[bottom_coord[0]][bottom_coord[1]]
             ];
}

function Grayscale(canvasName, data, rows, cols) {
  let canvas = document.getElementById(canvasName);
  let ctx = canvas.getContext("2d");

  let imageData = ctx.createImageData(cols, rows);

  for (let i = 0; i < data.length; i++) {
    const color = data[i];
    imageData.data[i * 4] = color;
    imageData.data[i * 4 + 1] = color;
    imageData.data[i * 4 + 2] = color;
    imageData.data[i * 4 + 3] = 255;
  }

  ctx.putImageData(imageData, 0, 0);
}

function RandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}


let terrainGrid = HeightMap(257, 255, 50);
terrainList = []
for (let q=0; q < 257; q++) {
  terrainList.push.apply(terrainList, terrainGrid[q])
}
Grayscale('grayscaleCanvas', terrainList, 257, 257);

我做的第一件事是将其更改为使用二维数组索引。这使得平方步的平均更容易(我能够更简单地环绕矩阵的边缘)。

我还更改了它以使用数组中的实际行值和列值,这比 block 坐标更简单。

我认为您最初没有正确计算平方步。您使用的 Angular 与菱形步相关,但您只是对方形步的 Angular 进行平均,而不是根据计算出的中点值和 Angular 的相关子集找到平均值(请参阅菱形方形上的维基百科图像以查看什么我是说)。

这是一个包含所有内容的 JSFiddle,希望这是您想要的。 (注意:如果有任何事情没有按惯用方式完成,那是因为我不擅长 Javascript):https://jsfiddle.net/z6so4xyc/15/

关于javascript - Diamond-Square 实现产生的值(value)太高,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53195820/

有关javascript - Diamond-Square 实现产生的值(value)太高的更多相关文章

  1. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  4. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. ruby-on-rails - Rails 单表继承 : How to override the value written to the type field - 2

    在我的系统中,我已经定义了STI。Dog继承自Animal,在animals表中有一个type列,其值为"Dog"。现在我想让SpecialDog继承自dog,只是为了在某些特殊情况下稍微修改一下行为。数据还是一样。我需要通过SpecialDog运行的所有查询,以返回数据库中类型为Dog的值。我的问题是因为我有一个type列,rails将WHERE"animals"."type"IN('SpecialDog')附加到我的查询中,所以我不能获取原始的Dog条目。所以我想要的是以某种方式覆盖rails在通过SpecialDog访问数据库时使用的值,使其表现得像Dog。有没有办法覆盖用于类型

  8. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. ruby - 获取数组中的值并最小化某个类属性的最优雅的方法是什么? - 2

    假设我有以下类(class):classPersondefinitialize(name,age)@name=name@age=ageenddefget_agereturn@ageendend我有一组Person对象。是否有一种简洁的、类似于Ruby的方法来获取最小(或最大)年龄的人?如何根据它对它们进行排序? 最佳答案 这样做会:people_array.min_by(&:get_age)people_array.max_by(&:get_age)people_array.sort_by(&:get_age)

随机推荐