Bagaimana cara mengalikan durasi dengan integer?

286

Untuk menguji goroutine bersamaan, saya menambahkan satu baris ke fungsi untuk membuatnya butuh waktu acak untuk kembali (hingga satu detik)

time.Sleep(rand.Int31n(1000) * time.Millisecond)

Namun ketika saya kompilasi, saya mendapatkan kesalahan ini

. \ crawler.go: 49: operasi tidak valid: rand.Int31n (1000) * time.Millisecond (tipe tidak cocok int32 dan waktu. Durasi)

Ada ide? Bagaimana saya bisa mengalikan durasi?

Kolonel Panic
sumber

Jawaban:

433

int32dan time.Durationberbagai jenis. Anda perlu mengonversi int32ke time.Duration, seperti time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond).

mna
sumber
5
Terima kasih, itu berhasil. Saya juga belajar tentang penyemaian generator angka acakrand.Seed(time.Now().Unix())
Kolonel Panic
44
hanya untuk pengetahuan saya, bagaimana cara kerja berikut ini? time.Sleep(time.Second * 2)
Ishan Khare
60
Ini bekerja karena konstanta memiliki tipe adaptif, berdasarkan pada bagaimana mereka digunakan. Lihat posting blog ini oleh Rob Pike yang menjelaskannya secara terperinci: blog.golang.org/constants
mna
28
Ini adalah salah satu dari hal-hal aneh yang terjadi karena sistem tipenya yang sederhana (kurangnya operator kelebihan dalam hal ini) - Anda harus melipatgandakan perkalian menjadi Duration* Duration= Duration, alih-alih yang asli yang sebenarnya lebih masuk akal: Duration* int= Duration.
Timmmm
14
Baik overloading operator atau konversi numerik implisit akan memungkinkan ini bekerja. Saya pikir mereka benar untuk membiarkan konversi numerik tersirat keluar. Setelah melihat kembali pada hal ini, itu int64(...) * Durationjauh lebih masuk akal daripada casting Duration, yang hanya merupakan pelanggaran mendasar tentang bagaimana unit harus bekerja. Sayangnya itu tidak berhasil. Anda benar-benar harus melakukan Duration * Durationyang mengerikan.
Timmmm
61

Anda harus melemparkannya ke format yang benar Playground.

yourTime := rand.Int31n(1000)
time.Sleep(time.Duration(yourTime) * time.Millisecond)

Jika Anda akan memeriksa dokumentasi untuk tidur , Anda melihat bahwa itu memerlukan func Sleep(d Duration)durasi sebagai parameter. Rand.Int31n Anda kembali int32.

Baris dari contoh berfungsi ( time.Sleep(100 * time.Millisecond)) karena kompiler cukup pintar untuk memahami bahwa di sini konstanta 100 Anda berarti durasi. Tetapi jika Anda melewati variabel, Anda harus melemparkannya.

Salvador Dali
sumber
16

Di Go, Anda bisa mengalikan variabel dari tipe yang sama, jadi Anda harus memiliki kedua bagian ekspresi dengan tipe yang sama.

Hal paling sederhana yang dapat Anda lakukan adalah melempar integer ke durasi sebelum mengalikan, tetapi itu akan melanggar semantik unit. Apa yang akan menjadi kelipatan durasi dengan durasi dalam satuan?

Saya lebih suka mengonversi waktu. Milidetik ke int64, dan kemudian mengalikannya dengan jumlah milidetik, lalu dilemparkan ke waktu. Durasi:

time.Duration(int64(time.Millisecond) * int64(rand.Int31n(1000)))

Dengan cara ini setiap bagian dari ekspresi dapat dikatakan memiliki nilai yang bermakna sesuai dengan tipenya. int64(time.Millisecond)bagian hanya nilai tanpa dimensi - jumlah unit terkecil dari nilai aslinya.

Jika berjalan di jalur yang sedikit lebih sederhana:

time.Duration(rand.Int31n(1000)) * time.Millisecond

Bagian kiri dari perkalian adalah omong kosong - nilai dari jenis "waktu. Durasi", memegang sesuatu yang tidak relevan dengan jenisnya:

numberOfMilliseconds := 100
// just can't come up with a name for following:
someLHS := time.Duration(numberOfMilliseconds)
fmt.Println(someLHS)
fmt.Println(someLHS*time.Millisecond)

Dan itu bukan hanya semantik, ada fungsi aktual yang terkait dengan jenis. Kode ini mencetak:

100ns
100ms

Menariknya, contoh kode di sini menggunakan kode paling sederhana, dengan semantik menyesatkan yang sama dari konversi Durasi: https://golang.org/pkg/time/#Duration

detik: = 10

fmt.Print (waktu. Durasi (detik) * waktu. Kedua) // mencetak 10s

George Polevoy
sumber
5

Sangat menyenangkan bahwa Go memiliki Durationtipe - memiliki unit yang didefinisikan secara eksplisit dapat mencegah masalah dunia nyata.

Dan karena aturan tipe Go yang ketat, Anda tidak bisa mengalikan Durasi dengan bilangan bulat - Anda harus menggunakan gips untuk mengalikan tipe umum.

/*
MultiplyDuration Hide semantically invalid duration math behind a function
*/
func MultiplyDuration(factor int64, d time.Duration) time.Duration {
    return time.Duration(factor) * d        // method 1 -- multiply in 'Duration'
 // return time.Duration(factor * int64(d)) // method 2 -- multiply in 'int64'
}

The dokumentasi resmi menunjukkan menggunakan metode # 1:

Untuk mengonversi jumlah integer unit ke Durasi, kalikan:

seconds := 10
fmt.Print(time.Duration(seconds)*time.Second) // prints 10s

Tapi, tentu saja, mengalikan durasi dengan durasi seharusnya tidak menghasilkan durasi - itu tidak masuk akal di wajah itu. Contoh kasus, 5 milidetik kali 5 milidetik menghasilkan 6h56m40s. Mencoba persegi 5 detik menghasilkan limpahan (dan bahkan tidak akan dikompilasi jika dilakukan dengan konstanta)

Omong-omong, int64representasi Durationdalam nanodetik "membatasi durasi terwakili terbesar hingga sekitar 290 tahun" , dan ini menunjukkan bahwa Duration, seperti int64, diperlakukan sebagai nilai yang ditandatangani:, (1<<(64-1))/(1e9*60*60*24*365.25) ~= 292dan itulah cara penerapannya:

// A Duration represents the elapsed time between two instants
// as an int64 nanosecond count. The representation limits the
// largest representable duration to approximately 290 years.
type Duration int64

Jadi, karena kita tahu bahwa representasi yang mendasarinya Durationadalah int64, melakukan pemeran di antara int64dan Durationmerupakan NO-OP yang masuk akal - hanya diperlukan untuk memenuhi aturan bahasa tentang jenis pencampuran, dan itu tidak berpengaruh pada operasi penggandaan selanjutnya.

Jika Anda tidak suka casting karena alasan kemurnian, kubur dalam panggilan fungsi seperti yang saya tunjukkan di atas.

bangsawan
sumber
+ msgstr "kalikan dengan / dengan tipe umum".
nobar
Diskusi serupa terkait dengan pembagian pada jawaban (salah) di sini .
Nobar
-3

Untuk penggandaan variabel ke waktu. Kedua menggunakan kode berikut

    oneHr:=3600
    addOneHrDuration :=time.Duration(oneHr)
    addOneHrCurrTime := time.Now().Add(addOneHrDuration*time.Second)
delroy.santos
sumber
Ini bukan cara yang baik untuk menggunakan time.Durationvariabel Go . Anda menyebutkan variabel addOneHrDurationwaktu Anda, time.Durationtetapi kemudian lanjutkan untuk mengaturnya menjadi 3600 ns bukan satu jam. A time.Durationkebetulan memiliki unit dasar nanodetik. Untuk benar-benar mendapatkan durasi satu jam Anda bisa melakukan sesuatu seperti: const oneHourDuration = 60 * time.Hour(atau 3600 * time.Secondatau time.Hour).
Dave C