Có một cơ chế để lặp x lần trong ES6 (ECMAScript 6) mà không có các biến có thể thay đổi không?


157

Cách điển hình để lặp xthời gian trong JavaScript là:

for (var i = 0; i < x; i++)
  doStuff(i);

Nhưng tôi không muốn sử dụng ++toán tử hoặc có bất kỳ biến nào có thể thay đổi được. Vì vậy, có một cách, trong ES6, để lặp lại xlần khác? Tôi yêu cơ chế của Ruby:

x.times do |i|
  do_stuff(i)
end

Bất cứ điều gì tương tự trong JavaScript / ES6? Tôi có thể gian lận và tạo ra máy phát điện của riêng mình:

function* times(x) {
  for (var i = 0; i < x; i++)
    yield i;
}

for (var i of times(5)) {
  console.log(i);
}

Tất nhiên tôi vẫn đang sử dụng i++. Ít nhất là ngoài tầm nhìn :), nhưng tôi hy vọng có một cơ chế tốt hơn trong ES6.


3
Tại sao biến điều khiển vòng lặp có thể thay đổi là một vấn đề? Chỉ là một nguyên tắc?
doldt

1
@doldt - Tôi đang cố gắng dạy JavaScript, nhưng tôi đang thử nghiệm trì hoãn khái niệm các biến có thể thay đổi cho đến sau này
tại.

5
Chúng tôi đang thực sự lạc đề ở đây, nhưng bạn có chắc chắn rằng việc chuyển sang các trình tạo ES6 (hoặc bất kỳ khái niệm cấp cao mới nào khác) là một ý tưởng hay trước khi họ tìm hiểu về các biến có thể thay đổi không? :)
từ

5
@doldt - có thể, tôi đang thử nghiệm. Sử dụng một cách tiếp cận ngôn ngữ chức năng cho JavaScript.
tại.

Sử dụng let để khai báo biến đó trong vòng lặp. Phạm vi của nó kết thúc với vòng lặp.
ncmathsadist

Câu trả lời:


156

ĐỒNG Ý!

Mã dưới đây được viết bằng cú pháp ES6 nhưng có thể dễ dàng được viết bằng ES5 hoặc thậm chí ít hơn. ES6 không phải là một yêu cầu để tạo ra một "cơ chế lặp x lần"


Nếu bạn không cần iterator trong cuộc gọi lại , đây là cách thực hiện đơn giản nhất

const times = x => f => {
  if (x > 0) {
    f()
    times (x - 1) (f)
  }
}

// use it
times (3) (() => console.log('hi'))

// or define intermediate functions for reuse
let twice = times (2)

// twice the power !
twice (() => console.log('double vision'))

Nếu bạn cần iterator , bạn có thể sử dụng hàm bên trong có tên với tham số bộ đếm để lặp lại cho bạn

const times = n => f => {
  let iter = i => {
    if (i === n) return
    f (i)
    iter (i + 1)
  }
  return iter (0)
}

times (3) (i => console.log(i, 'hi'))


Dừng đọc ở đây nếu bạn không thích học thêm nhiều thứ ...

Nhưng một cái gì đó nên cảm thấy về những ...

  • ifbáo cáo chi nhánh duy nhất là xấu xí - điều gì xảy ra trên các chi nhánh khác?
  • nhiều câu lệnh / biểu thức trong các cơ quan chức năng - các mối quan tâm về thủ tục có bị trộn lẫn không?
  • ngầm trả lại undefined- dấu hiệu của tạp chất, chức năng phụ

"Không có cách nào tốt hơn à?"

Có. Trước tiên hãy xem lại triển khai ban đầu của chúng tôi

// times :: Int -> (void -> void) -> void
const times = x => f => {
  if (x > 0) {
    f()               // has to be side-effecting function
    times (x - 1) (f)
  }
}

Chắc chắn, nó đơn giản, nhưng chú ý cách chúng ta chỉ gọi f()và không làm gì với nó. Điều này thực sự giới hạn loại chức năng chúng ta có thể lặp lại nhiều lần. Ngay cả khi chúng ta có sẵn trình lặp, thì f(i)nó vẫn không linh hoạt hơn nhiều.

Điều gì xảy ra nếu chúng ta bắt đầu với một loại thủ tục lặp lại chức năng tốt hơn? Có lẽ một cái gì đó làm cho việc sử dụng đầu vào và đầu ra tốt hơn.

Lặp lại chức năng chung

// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
  if (n > 0)
    return repeat (n - 1) (f) (f (x))
  else
    return x
}

// power :: Int -> Int -> Int
const power = base => exp => {
  // repeat <exp> times, <base> * <x>, starting with 1
  return repeat (exp) (x => base * x) (1)
}

console.log(power (2) (8))
// => 256

Ở trên, chúng tôi đã định nghĩa một repeathàm chung có một đầu vào bổ sung được sử dụng để bắt đầu ứng dụng lặp lại của một hàm duy nhất.

// repeat 3 times, the function f, starting with x ...
var result = repeat (3) (f) (x)

// is the same as ...
var result = f(f(f(x)))

Thực hiện timesvớirepeat

Vâng, điều này bây giờ dễ dàng; gần như tất cả các công việc đã được thực hiện.

// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
  if (n > 0)
    return repeat (n - 1) (f) (f (x))
  else
    return x
}

// times :: Int -> (Int -> Int) -> Int 
const times = n=> f=>
  repeat (n) (i => (f(i), i + 1)) (0)

// use it
times (3) (i => console.log(i, 'hi'))

Vì hàm của chúng ta lấy ilàm đầu vào và trả về i + 1, nên hàm này hoạt động hiệu quả như là trình lặp của chúng ta mà chúng ta chuyển đếnf mỗi lần.

Chúng tôi cũng đã sửa danh sách các vấn đề của mình

  • Không còn chi nhánh xấu xí if báo cáo
  • Các cơ quan biểu hiện đơn biểu thị mối quan tâm riêng biệt
  • Không còn vô dụng, hoàn toàn trở lại undefined

Toán tử dấu phẩy,

Trong trường hợp bạn gặp khó khăn khi xem ví dụ cuối hoạt động như thế nào, thì nó phụ thuộc vào nhận thức của bạn về một trong những trục chiến đấu lâu đời nhất của JavaScript; các toán tử dấu phẩy - trong ngắn hạn, nó đánh giá biểu thức từ trái sang phải và trả về giá trị của biểu thức đánh giá cuối cùng

(expr1 :: a, expr2 :: b, expr3 :: c) :: c

Trong ví dụ trên của chúng tôi, tôi đang sử dụng

(i => (f(i), i + 1))

đó chỉ là một cách viết ngắn gọn

(i => { f(i); return i + 1 })

Tối ưu hóa cuộc gọi đuôi

Cũng hấp dẫn như các triển khai đệ quy, tại thời điểm này, tôi sẽ vô trách nhiệm khi đề xuất với họ rằng không có JavaScript VM nào tôi có thể nghĩ đến việc hỗ trợ loại bỏ cuộc gọi đuôi thích hợp - babel đã sử dụng để phiên dịch nó, nhưng nó đã bị "phá vỡ; "Tình trạng trong hơn một năm.

repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded

Vì vậy, chúng ta nên xem lại việc thực hiện repeat để làm cho nó an toàn.

Mã dưới đây không sử dụng các biến có thể thay đổi nxlưu ý rằng tất cả các đột biến được định vị cho repeathàm - không có thay đổi trạng thái (đột biến) nào được nhìn thấy từ bên ngoài hàm

// repeat :: Int -> (a -> a) -> (a -> a)
const repeat = n => f => x =>
  {
    let m = 0, acc = x
    while (m < n)
      (m = m + 1, acc = f (acc))
    return acc
  }

// inc :: Int -> Int
const inc = x =>
  x + 1

console.log (repeat (1e8) (inc) (0))
// 100000000

Điều này sẽ có rất nhiều bạn nói "nhưng đó không phải là chức năng!" - Tôi biết, cứ thư giãn đi. Chúng ta có thể thực hiện giao diện loop/ kiểu Clojure recurcho vòng lặp không gian không đổi bằng cách sử dụng các biểu thức thuần túy ; không có những whilethứ đó

Ở đây chúng tôi trừu tượng hóa whilevới loopchức năng của chúng tôi - nó tìm kiếm một recurloại đặc biệt để giữ cho vòng lặp chạy. Khi không recurgặp loại, vòng lặp kết thúc và kết quả tính toán được trả về

const recur = (...args) =>
  ({ type: recur, args })
  
const loop = f =>
  {
    let acc = f ()
    while (acc.type === recur)
      acc = f (...acc.args)
    return acc
  }

const repeat = $n => f => x =>
  loop ((n = $n, acc = x) =>
    n === 0
      ? acc
      : recur (n - 1, f (acc)))
      
const inc = x =>
  x + 1

const fibonacci = $n =>
  loop ((n = $n, a = 0, b = 1) =>
    n === 0
      ? a
      : recur (n - 1, b, a + b))
      
console.log (repeat (1e7) (inc) (0)) // 10000000
console.log (fibonacci (100))        // 354224848179262000000


24
Có vẻ quá phức tạp (tôi đặc biệt bối rối g => g(g)(x)). Có một lợi ích từ một hàm bậc cao hơn so với hàm bậc nhất, như trong giải pháp của tôi không?
Pavlo

1
@naomik: cảm ơn vì đã dành thời gian để đăng một liên kết. Nhiều đánh giá cao.
Pineda

1
@ AlfonsoPérez Tôi đánh giá cao nhận xét. Tôi sẽ xem liệu tôi có thể làm việc một chút gợi ý ở đó ở đâu đó không ^ _ ^
Cảm ơn bạn vào

1
@naomik Vĩnh biệt TCO ! Tôi đang bị tàn phá.

10
Có vẻ như câu trả lời này được chấp nhận và đánh giá tốt bởi vì nó phải mất rất nhiều nỗ lực, nhưng tôi không nghĩ đó là một câu trả lời hay. Câu trả lời đúng cho câu hỏi là "không". Sẽ rất hữu ích khi liệt kê một cách giải quyết như bạn đã làm, nhưng ngay sau đó bạn nói rằng có một cách tốt hơn. Tại sao bạn không đặt câu trả lời đó và loại bỏ câu trả lời tệ hơn ở trên cùng? Tại sao bạn giải thích các toán tử dấu phẩy? Tại sao bạn lại mang Clojure lên? Tại sao, nói chung, rất nhiều tiếp tuyến cho một câu hỏi với câu trả lời 2 ký tự? Các câu hỏi đơn giản không chỉ là một nền tảng để người dùng trình bày về một số sự kiện lập trình gọn gàng.
Timofey 'Sasha' Kondrashov

266

Sử dụng toán tử lây lan ES2015 :

[...Array(n)].map()

const res = [...Array(10)].map((_, i) => {
  return i * 10;
});

// as a one liner
const res = [...Array(10)].map((_, i) => i * 10);

Hoặc nếu bạn không cần kết quả:

[...Array(10)].forEach((_, i) => {
  console.log(i);
});

// as a one liner
[...Array(10)].forEach((_, i) => console.log(i));

Hoặc sử dụng toán tử ES2015 Array.from :

Array.from(...)

const res = Array.from(Array(10)).map((_, i) => {
  return i * 10;
});

// as a one liner
const res = Array.from(Array(10)).map((_, i) => i * 10);

Lưu ý rằng nếu bạn chỉ cần một chuỗi lặp lại, bạn có thể sử dụng String.prototype.repeat .

console.log("0".repeat(10))
// 0000000000

26
Tốt hơn:Array.from(Array(10), (_, i) => i*10)
Bergi

6
Đây phải là câu trả lời tốt nhất. Vậy ES6! Tuyệt vời nhiều!
Gergely Fehérvári

3
Nếu bạn không cần iterator (i), bạn có thể loại trừ cả khóa và giá trị để thực hiện điều này:[...Array(10)].forEach(() => console.log('looping 10 times');
Sterling Bourne

9
Vì vậy, bạn phân bổ toàn bộ mảng của các yếu tố N chỉ để ném nó đi?
Kugel

2
Có ai đã giải quyết bình luận trước đó của Kugel? Tôi đã tự hỏi điều tương tự
Arman

37
for (let i of Array(100).keys()) {
    console.log(i)
}

Điều này làm việc, vì vậy đó là tuyệt vời! Nhưng có một chút xấu xí theo nghĩa là cần thêm công việc và đây không phải là những gì Arraycác phím được sử dụng cho.
tại.

@at. thật. Nhưng tôi không chắc có từ đồng nghĩa của haskell [0..x]trong JS ngắn gọn hơn trong câu trả lời của tôi.
zerkms

bạn có thể đúng rằng không có gì ngắn gọn hơn thế này.
tại.

OK, tôi hiểu lý do tại sao công việc này đưa ra sự khác biệt giữa Array.prototype.keysObject.prototype.keys, nhưng nó chắc chắn là khó hiểu ngay từ cái nhìn đầu tiên.
Mark Reed

1
@cchamberlain với TCO trong ES2015 (mặc dù không được triển khai ở bất cứ đâu?) nó có thể là mối quan tâm ít hơn, nhưng thực sự :-)
zerkms

29

Tôi nghĩ rằng giải pháp tốt nhất là sử dụng let:

for (let i=0; i<100; i++) 

Điều đó sẽ tạo ra một biến mới (có thể thay đổi) icho mỗi đánh giá cơ thể và đảm bảo rằng ichỉ được thay đổi trong biểu thức tăng trong cú pháp vòng lặp đó, không phải từ bất kỳ nơi nào khác.

Tôi có thể loại gian lận và tạo ra máy phát điện của riêng tôi. Ít nhất i++là khuất mắt :)

Điều đó là đủ imo. Ngay cả trong các ngôn ngữ thuần túy, tất cả các hoạt động (hoặc ít nhất, thông dịch viên của chúng) được xây dựng từ các nguyên thủy sử dụng đột biến. Chừng nào nó còn đúng phạm vi, tôi không thể thấy điều gì sai với điều đó.

Bạn sẽ ổn với

function* times(n) {
  for (let i = 0; i < x; i++)
    yield i;
}
for (const i of times(5))
  console.log(i);

Nhưng tôi không muốn sử dụng ++toán tử hoặc có bất kỳ biến nào có thể thay đổi được.

Sau đó, lựa chọn duy nhất của bạn là sử dụng đệ quy. Bạn cũng có thể định nghĩa hàm tạo mà không có khả năng biến đổi i:

function* range(i, n) {
  if (i >= n) return;
  yield i;
  return yield* range(i+1, n);
}
times = (n) => range(0, n);

Nhưng điều đó có vẻ quá mức đối với tôi và có thể có vấn đề về hiệu suất (vì loại bỏ cuộc gọi đuôi không có sẵn return yield*).


1
Tôi thích tùy chọn này - đẹp và đơn giản!
DanV

2
Điều này đơn giản và đi vào vấn đề và không phân bổ một mảng như nhiều câu trả lời ở trên
Kugel

@Kugel Người thứ hai có thể phân bổ trên stack, tuy nhiên
Bergi

Điểm tốt không chắc chắn nếu tối ưu hóa cuộc gọi đuôi sẽ hoạt động ở đây @Bergi
Kugel



11

Trả lời: ngày 09 tháng 12 năm 2015

Cá nhân, tôi tìm thấy câu trả lời được chấp nhận cả ngắn gọn (tốt) và ngắn gọn (xấu). Đánh giá cao tuyên bố này có thể là chủ quan, vì vậy xin vui lòng đọc câu trả lời này và xem nếu bạn đồng ý hay không đồng ý

Ví dụ được đưa ra trong câu hỏi là một cái gì đó giống như của Ruby:

x.times do |i|
  do_stuff(i)
end

Thể hiện điều này trong JS bằng cách sử dụng dưới đây sẽ cho phép:

times(x)(doStuff(i));

Đây là mã:

let times = (n) => {
  return (f) => {
    Array(n).fill().map((_, i) => f(i));
  };
};

Đó là nó!

Cách sử dụng ví dụ đơn giản:

let cheer = () => console.log('Hip hip hooray!');

times(3)(cheer);

//Hip hip hooray!
//Hip hip hooray!
//Hip hip hooray!

Ngoài ra, sau các ví dụ về câu trả lời được chấp nhận:

let doStuff = (i) => console.log(i, ' hi'),
  once = times(1),
  twice = times(2),
  thrice = times(3);

once(doStuff);
//0 ' hi'

twice(doStuff);
//0 ' hi'
//1 ' hi'

thrice(doStuff);
//0 ' hi'
//1 ' hi'
//2 ' hi'

Lưu ý bên - Xác định chức năng phạm vi

Một câu hỏi tương tự / liên quan, sử dụng các cấu trúc mã rất giống nhau về cơ bản, có thể có một hàm Phạm vi thuận tiện trong JavaScript (lõi), một cái gì đó tương tự như hàm phạm vi của gạch dưới.

Tạo một mảng với n số, bắt đầu từ x

Gạch dưới

_.range(x, x + n)

ES2015

Một vài lựa chọn thay thế:

Array(n).fill().map((_, i) => x + i)

Array.from(Array(n), (_, i) => x + i)

Bản trình diễn sử dụng n = 10, x = 1:

> Array(10).fill().map((_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

> Array.from(Array(10), (_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

Trong một thử nghiệm nhanh tôi đã chạy, với mỗi lần chạy trên một triệu lần mỗi lần sử dụng hàm giải pháp và hàm doStuff của chúng tôi, cách tiếp cận trước đây (Array (n) .fill ()) đã chứng minh nhanh hơn một chút.


8
Array(100).fill().map((_,i)=> console.log(i) );

Phiên bản này đã làm rõ yêu cầu của OP về tính bất biến. Cũng xem xét sử dụng reducethay vìmap tùy thuộc vào trường hợp sử dụng của bạn.

Đây cũng là một lựa chọn nếu bạn không ngại một chút đột biến trong nguyên mẫu của mình.

Number.prototype.times = function(f) {
   return Array(this.valueOf()).fill().map((_,i)=>f(i));
};

Bây giờ chúng ta có thể làm điều này

((3).times(i=>console.log(i)));

+1 để arcseldon cho .fillđề xuất.


Bỏ phiếu xuống, vì phương thức điền không được hỗ trợ trong IE hoặc Opera hoặc PhantomJS
morhook

8

Đây là một lựa chọn tốt khác:

Array.from({ length: 3}).map(...);

Tốt hơn là, như @Dave Morse đã chỉ ra trong các bình luận, bạn cũng có thể thoát khỏi mapcuộc gọi, bằng cách sử dụng tham số thứ hai của Array.fromhàm như vậy:

Array.from({ length: 3 }, () => (...))


2
Đây phải là câu trả lời được chấp nhận! Một gợi ý nhỏ - bạn đã có được chức năng giống như bản đồ mà bạn cần miễn phí với Array.from: Array.from({ length: label.length }, (_, i) => (...)) Điều này giúp tiết kiệm việc tạo một mảng tạm thời trống chỉ để thực hiện cuộc gọi lên bản đồ.
Dave Morse

7

Không phải thứ tôi sẽ dạy (hoặc đã từng sử dụng trong mã của mình), nhưng đây là một giải pháp xứng đáng với tiền mã hóa mà không làm thay đổi một biến, không cần ES6:

Array.apply(null, {length: 10}).forEach(function(_, i){
    doStuff(i);
})

Thực sự nhiều hơn một điều chứng minh khái niệm thú vị hơn là một câu trả lời hữu ích, thực sự.


Coud Array.apply(null, {length: 10})không được Array(10)?
Pavlo

1
@Pavlo, thực sự, không. Mảng (10) sẽ tạo ra một mảng có độ dài 10, nhưng không có bất kỳ khóa nào được xác định trong nó, điều này làm cho cấu trúc forEach không thể sử dụng được trong trường hợp này. Nhưng thực sự nó có thể được đơn giản hóa nếu bạn không sử dụng forEach, xem câu trả lời của zerkms (mặc dù sử dụng ES6!).
doldt

sáng tạo @doldt, nhưng tôi đang tìm kiếm thứ gì đó dễ dạy và đơn giản.
tại.

5

Tôi đến bữa tiệc muộn, nhưng vì câu hỏi này xuất hiện thường xuyên trong kết quả tìm kiếm, tôi chỉ muốn thêm một giải pháp mà tôi cho là tốt nhất về khả năng đọc trong khi không dài (lý tưởng cho bất kỳ IMO codebase nào) . Nó đột biến, nhưng tôi sẽ đánh đổi các nguyên tắc KISS.

let times = 5
while( times-- )
    console.log(times)
// logs 4, 3, 2, 1, 0

3
Cảm ơn bạn đã là tiếng nói của lý trí trong những gì tôi chỉ có thể mô tả như một bữa tiệc tôn sùng lambda cấp cao hơn. Tôi cũng đã kết thúc câu hỏi và trả lời này sau một lần truy cập đầu tiên vô hại trên Google và nhanh chóng khiến tôi tỉnh táo bởi hầu hết các câu trả lời ở đây. Của bạn là người đầu tiên trong danh sách mà tôi sẽ xem xét một giải pháp đơn giản cho một vấn đề đơn giản.
Martin Devillers

Vấn đề duy nhất là nó hơi phản trực giác nếu bạn muốn sử dụng timesbiến bên trong vòng lặp. Có lẽ countdownsẽ là một cách đặt tên tốt hơn. Nếu không, câu trả lời rõ ràng và rõ ràng nhất trên trang.
Tony Brasunas

3

Afaik, không có cơ chế nào trong ES6 tương tự như timesphương pháp của Ruby . Nhưng bạn có thể tránh đột biến bằng cách sử dụng đệ quy:

let times = (i, cb, l = i) => {
  if (i === 0) return;

  cb(l - i);
  times(i - 1, cb, l);
}

times(5, i => doStuff(i));

Bản trình diễn: http://jsbin.com/koyecovano/1/edit?js,console


Tôi thích cách tiếp cận này, tôi thích đệ quy. Nhưng tôi thích thứ gì đó đơn giản hơn để hiển thị các vòng lặp người dùng JavaScript mới.
tại.

3

Nếu bạn sẵn sàng sử dụng một thư viện, cũng có lodash_.times hoặc gạch dưới_.times :

_.times(x, i => {
   return doStuff(i)
})

Lưu ý điều này trả về một mảng kết quả, vì vậy nó thực sự giống như viên ruby ​​này:

x.times.map { |i|
  doStuff(i)
}

2

Trong mô hình chức năng repeatthường là một hàm đệ quy vô hạn. Để sử dụng nó, chúng ta cần đánh giá lười biếng hoặc tiếp tục chuyển kiểu.

Lười đánh giá chức năng lặp lại

const repeat = f => x => [x, () => repeat(f) (f(x))];
const take = n => ([x, f]) => n === 0 ? x : take(n - 1) (f());

console.log(
  take(8) (repeat(x => x * 2) (1)) // 256
);

Tôi sử dụng một thunk (một chức năng không có đối số) để đạt được đánh giá lười biếng trong Javascript.

Chức năng lặp lại với kiểu truyền tiếp

const repeat = f => x => [x, k => k(repeat(f) (f(x)))];
const take = n => ([x, k]) => n === 0 ? x : k(take(n - 1));

console.log(
  take(8) (repeat(x => x * 2) (1)) // 256
);

CPS là một chút đáng sợ lúc đầu. Tuy nhiên, nó luôn tuân theo cùng một mẫu: Đối số cuối cùng là phần tiếp theo (một hàm), gọi thân thể của chính nó : k => k(...). Xin lưu ý rằng CPS biến ứng dụng từ trong ra ngoài, tức là take(8) (repeat...)trở thành k(take(8)) (...)nơi kđược áp dụng một phần repeat.

Phần kết luận

Bằng cách tách sự lặp lại ( repeat) khỏi điều kiện kết thúc ( take), chúng ta có được sự linh hoạt - tách các mối quan tâm cho đến tận cùng cay đắng của nó: D


1

Ưu điểm của giải pháp này

  • Đơn giản nhất để đọc / sử dụng (imo)
  • Giá trị trả về có thể được sử dụng dưới dạng tổng hoặc chỉ cần bỏ qua
  • Phiên bản es6 đơn giản, cũng liên kết đến phiên bản TypeScript của mã

Nhược điểm - Đột biến. Là nội bộ chỉ tôi không quan tâm, có thể một số người khác sẽ không.

Ví dụ và mã

times(5, 3)                       // 15    (3+3+3+3+3)

times(5, (i) => Math.pow(2,i) )   // 31    (1+2+4+8+16)

times(5, '<br/>')                 // <br/><br/><br/><br/><br/>

times(3, (i, count) => {          // name[0], name[1], name[2]
    let n = 'name[' + i + ']'
    if (i < count-1)
        n += ', '
    return n
})

function times(count, callbackOrScalar) {
    let type = typeof callbackOrScalar
    let sum
    if (type === 'number') sum = 0
    else if (type === 'string') sum = ''

    for (let j = 0; j < count; j++) {
        if (type === 'function') {
            const callback = callbackOrScalar
            const result = callback(j, count)
            if (typeof result === 'number' || typeof result === 'string')
                sum = sum === undefined ? result : sum + result
        }
        else if (type === 'number' || type === 'string') {
            const scalar = callbackOrScalar
            sum = sum === undefined ? scalar : sum + scalar
        }
    }
    return sum
}

Phiên bản TypeScipt
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011


0

giải quyết các khía cạnh chức năng:

function times(n, f) {
    var _f = function (f) {
        var i;
        for (i = 0; i < n; i++) {
            f(i);
        }
    };
    return typeof f === 'function' && _f(f) || _f;
}
times(6)(function (v) {
    console.log('in parts: ' + v);
});
times(6, function (v) {
    console.log('complete: ' + v);
});

5
"Giải quyết các khía cạnh chức năng" và sau đó sử dụng vòng lặp bắt buộc với một biến đổi i. Lý do để thậm chí sử dụng timestrên đồng bằng cũ forlà gì?
zerkms

tái sử dụng như var twice = times(2);.
Nina Scholz

Vậy tại sao không chỉ sử dụng forhai lần?
zerkms

tôi không sợ sử dụng cho. câu hỏi là một cái gì đó không sử dụng variabele. nhưng kết quả luôn luôn là một số loại bộ nhớ đệm aka biến.
Nina Scholz

1
"là thứ không nên sử dụng variabele" --- và bạn vẫn sử dụng nó - i++. Không rõ ràng làm thế nào gói một cái gì đó không thể chấp nhận trong một chức năng làm cho nó tốt hơn.
zerkms

0

Máy phát điện? Đệ quy? Tại sao rất nhiều hatin 'trên mutatin'? ;-)

Nếu nó được chấp nhận miễn là chúng ta "ẩn" nó, thì bạn chỉ cần chấp nhận sử dụng toán tử đơn nguyên và chúng ta có thể giữ mọi thứ đơn giản :

Number.prototype.times = function(f) { let n=0 ; while(this.valueOf() > n) f(n++) }

Giống như trong ruby:

> (3).times(console.log)
0
1
2

2
Thumbs up: "Tại sao rất nhiều hatin 'trên mutatin'?"
Sarreph

1
Thumbs up cho đơn giản, ngón tay cái xuống để đi theo phong cách ruby ​​một chút quá nhiều với khỉ. Chỉ cần nói không với những con khỉ xấu xấu.
mrm

1
@mrm là "bản vá khỉ" này, đây không phải là một trường hợp mở rộng sao? Embrace & mở rộng :)
Conny

Không, việc thêm các hàm vào Số (hoặc Chuỗi hoặc Mảng hoặc bất kỳ lớp nào khác mà bạn không phải là tác giả), theo định nghĩa, là các bản vá polyfill hoặc khỉ - và thậm chí không nên sử dụng polyfill. Đọc các định nghĩa của "bản vá khỉ", "polyfill" và một cách thay thế được đề xuất là "ponyfill". Đó là những gì bạn muốn.
thưa ngài

Để mở rộng Số bạn sẽ làm: class SuperNumber kéo dài Số {lần (fn) {for (let i = 0; i <this; i ++) {fn (i); }}}
Alexander

0

Tôi gói câu trả lời của @Tieme với chức năng trợ giúp.

Trong TypeScript:

export const mapN = <T = any[]>(count: number, fn: (...args: any[]) => T): T[] => [...Array(count)].map((_, i) => fn())

Bây giờ bạn có thể chạy:

const arr: string[] = mapN(3, () => 'something')
// returns ['something', 'something', 'something']

0

Toi lam cai nay:

function repeat(func, times) {
    for (var i=0; i<times; i++) {
        func(i);
    }
}

Sử dụng:

repeat(function(i) {
    console.log("Hello, World! - "+i);
}, 5)

/*
Returns:
Hello, World! - 0
Hello, World! - 1
Hello, World! - 2
Hello, World! - 3
Hello, World! - 4
*/

Các ibiến trả về số lần nó đã looped - hữu ích nếu bạn cần phải tải trước một số lượng x của hình ảnh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.