JavaScript 趣味编程:从基础循环到函数式,解锁打印三角形的 N 种姿势
对于许多程序员来说,用代码在控制台打印出一个小小的三角形,是学习生涯中一次难忘的"开窍"时刻。它不仅仅是一个简单的练习,更是我们理解循环、逻辑和模式识别的起点。
今天,我们不只满足于实现它,而是要玩出花样。本文将带你从最经典的双重 for
循环,一路走到更现代、更优雅的函数式写法,让你彻底掌握打印三角形的各种"姿势"。
第一站:梦开始的地方 ------ 直角三角形
我们的第一个目标是打印出这样的形状(以5行为例):
markdown
*
**
***
****
*****
写法一:双重 for
循环------经典永不过时
这是每个初学者都会接触到的方法,它完美地诠释了嵌套循环的魅力。
javascript
function printRightTriangleFor(rows) {
// 外层循环控制行数 (i)
for (let i = 1; i <= rows; i++) {
let rowString = '';
// 内层循环控制每一行打印的星号数量 (j)
// 第 i 行,就有 i 个星号
for (let j = 1; j <= i; j++) {
rowString += '*';
}
console.log(rowString);
}
}
printRightTriangleFor(5);
思路解析: 外层循环
i
像一个"行长",负责决定当前是第几行。内层循环j
是一个"工人",i
告诉它这一行需要多少个*
,它就勤勤恳恳地拼接多少个。简单、直观,逻辑清晰。
写法二:巧用 .repeat()
------ 告别内层循环
进入现代 JavaScript 的世界,我们可以用更简洁的方式完成同样的工作。ES6 的 String.prototype.repeat()
方法简直是为此而生。
javascript
function printRightTriangleRepeat(rows) {
for (let i = 1; i <= rows; i++) {
// 在第 i 行,直接生成一个重复 i 次的 '*' 字符串
console.log('*'.repeat(i));
}
}
printRightTriangleRepeat(5);
思路解析: 这种写法的代码量骤减,意图也更加明确。我们不再需要手动去拼接字符串,而是直接告诉 JavaScript:"给我一个包含
i
个星号的字符串"。代码更具可读性,也更"酷"。
写法三:函数式编程------换个"次元"看问题
如果你想让你的代码看起来更"极客",不妨试试函数式的写法。它不使用显式的循环,而是通过数组方法来生成结果。
javascript
function printRightTriangleFunctional(rows) {
const triangle = Array.from(
{ length: rows }, // 1. 创建一个指定长度的"空"数组
(_, index) => '*'.repeat(index + 1) // 2. 映射每一项为所需的字符串
).join('\n'); // 3. 用换行符拼接成最终成品
console.log(triangle);
}
printRightTriangleFunctional(5);
思路解析: 这种方式将问题分解为三步:创建一个代表"行"的数组,将每一"行"映射成对应的星号字符串,最后将所有行合并。这是一种声明式的编程风格,你只关心"做什么",而不是"怎么做",代码非常优雅。
第二站:挑战升级 ------ 等腰三角形(金字塔)
现在,让我们来点更有挑战性的。打印一个居中的金字塔(以 6 行为例)。
markdown
*
***
*****
*******
*********
***********
逻辑分析: 这次,每一行不仅有星号,还有前导空格。通过观察规律,我们发现对于第 i
行(i
从 1 开始):
- 空格数量 =
总行数 - i
- 星号数量 =
(2 * i) - 1
有了这个公式,我们可以解锁多种实现方式。
姿势一:单循环 + .repeat()
(最常用)
这是最直接、清晰的实现方式,将上面的数学公式直接翻译成代码。
javascript
function printIsoscelesTriangle(rows) {
for (let i = 1; i <= rows; i++) {
// 计算并生成前导空格
const spaces = ' '.repeat(rows - i);
// 计算并生成星号
const stars = '*'.repeat((2 * i) - 1);
// 拼接并打印
console.log(spaces + stars);
}
}
printIsoscelesTriangle(6);
姿势二:函数式 Array.from
+ map
延续直角三角形的函数式思路,我们同样可以一行代码解决问题。
javascript
function pyramidFunctional(n) {
const rows = Array.from({ length: n }, (_, idx) => {
const i = idx + 1;
return ' '.repeat(n - i) + '*'.repeat(2 * i - 1);
});
console.log(rows.join('\n'));
}
pyramidFunctional(6);
姿势三:递归生成
对于喜欢挑战的同学,递归也是一个有趣的选择。
javascript
function pyramidRecursive(n, i = 1, out = []) {
if (i > n) {
console.log(out.join('\n'));
return;
}
out.push(' '.repeat(n - i) + '*'.repeat(2 * i - 1));
pyramidRecursive(n, i + 1, out);
}
pyramidRecursive(6);
更多姿势一览
JavaScript 的灵活性远不止于此,我们还可以使用 while
循环、reduce
归纳、甚至是 Generator
(生成器)来实现同样的效果。
-
while
循环版本:javascriptfunction pyramidWhile(n) { let i = 1; while (i <= n) { console.log(' '.repeat(n - i) + '*'.repeat(2 * i - 1)); i++; } } pyramidWhile(6);
-
生成器 (Generator) 逐行输出:
javascriptfunction* pyramidGen(n) { for (let i = 1; i <= n; i++) { yield ' '.repeat(n - i) + '*'.repeat(2 * i - 1); } } for (const line of pyramidGen(6)) { console.log(line); }
最终站:封装归纳 ------ 打造一个通用的图形打印机
作为一名优秀的开发者,我们追求的是代码的复用性。让我们把上面的逻辑封装成一个更通用的函数,让它能打印不同类型、不同行数、甚至不同字符的图形。
javascript
/**
* 打印各种形状的函数
* @param {number} rows - 总行数
* @param {string} shape - 'right', 'inverted-right', 'isosceles'
* @param {string} char - 用于打印的字符,默认为 '*'
*/
function printShape(rows, shape = 'right', char = '*') {
console.log(`\n--- 打印 ${rows} 行的 ${shape} 三角形 (字符: ${char}) ---`);
switch (shape) {
case 'right':
for (let i = 1; i <= rows; i++) console.log(char.repeat(i));
break;
case 'inverted-right': // 新增倒立直角
for (let i = rows; i >= 1; i--) console.log(char.repeat(i));
break;
case 'isosceles':
for (let i = 1; i <= rows; i++) {
const spaces = ' '.repeat(rows - i);
const chars = char.repeat((2 * i) - 1);
console.log(spaces + chars);
}
break;
default:
console.log('抱歉,我还不会打印这种形状。');
}
}
// 让我们来试试这个强大的打印机!
printShape(7, 'right', '#');
printShape(6, 'isosceles', '+');
printShape(5, 'inverted-right'); // 打印倒立直角三角形
总结
从一个简单的打印三角形问题,我们回顾了经典的循环逻辑,学习了现代 JavaScript 的简洁语法,体验了函数式编程的优雅,并最终将其封装成一个可复用的工具函数。
这正是编程的乐趣所在:同一个问题,总有更多、更好、更有趣的解法等待我们去发现。
现在,轮到你了!你还能想到哪些有趣的写法?或者,你能挑战一下打印一个菱形或者空心三角形吗?