Berapa banyak argumen yang diajukan?


33

Dengan menggunakan bahasa pilihan Anda, tulislah sebuah fungsi yang mengambil sejumlah variabel argumen dan mengembalikan jumlah argumen yang dipanggil dengannya.

Spesifik:

  • Bahasa Anda perlu mendukung fungsi argumen variadik: sesuatu yang dapat dipanggil yang mengambil jumlah argumen sewenang-wenang dan mengembalikan nilai.
  • Parameter harus dapat dilewati secara individual. Ini berarti bahwa melewatkan array hanya akan dihitung untuk satu parameter. Anda dapat menggunakan larik "semua argumen yang berlalu" jika bahasa Anda mendukungnya; batasannya adalah bagaimana fungsi dipanggil.
  • Kode yang memanggil fungsi ini tidak harus diharuskan untuk melewati jumlah argumen di sumbernya . Jika kompiler memasukkan jumlah argumen sebagai bagian dari konvensi pemanggilan, itu diperbolehkan.
  • Argumen dapat berupa jenis apa pun yang Anda inginkan. Anda dapat mendukung hanya satu tipe (mis. Hanya pendukung intyang masih valid), tipe arbitrer (semua jenis argumen diperbolehkan), atau kombinasi jenis argumen apa pun (mis. Argumen pertama adalah int, sisanya adalah string).
  • Fungsi Anda mungkin memiliki jumlah argumen maksimum (terutama karena sumber daya terbatas), tetapi harus mendukung setidaknya 2 argumen.

Sampel:

  • f() kembali 0
  • f(1)atau f("a")kembali1
  • f([1, 2, 3])kembali 1saat dilewatkan array, bukan 3 argumen
  • f(1, 10)atau f(1, "a")kembali2

Karena ini adalah kode-golf, solusi yang menang adalah yang menggunakan jumlah byte paling sedikit.


4
Tidak sepenuhnya jelas (secara objektif) apa yang dimaksud dengan "fungsi", "nilai pengembalian" atau "argumen variadik". Misalnya, apakah fungsi Dodos dianggap sebagai monadik atau variadik?
user202729

24
@ user202729 Jika bahasa Anda tidak mendukung fungsi, gunakan bahasa lain. Ini bukan persyaratan bahwa semua bahasa dapat bersaing, bagian dari kode golf adalah menemukan alat yang tepat untuk pekerjaan itu.
Sanchises

5
@ user202729 Saya tidak punya masalah dengan tantangan sesekali yang ditujukan pada bahasa tradisional / tingkat tinggi, sama seperti kita memiliki tantangan sesekali yang hanya mungkin terjadi dalam bahasa yang tidak biasa.
Sanchises

6
tidak tahu kami harus menyelesaikan masalah penghentian untuk karakteristik bahasa agar memiliki tantangan yang jelas ....
Conor O'Brien

5
Jika bahasa Anda tidak memiliki konsep argumen / konvensi pemanggilan maka bahasa tersebut tidak sesuai dengan kriteria yang mendukung jumlah argumen yang berubah-ubah.
Glenn Smith

Jawaban:


15

Amstrad CPC Z80 panggilan biner dari BASIC, 1 byte, hex encoded

C9          : RET

(Juga versi 2 dan 5 byte, lihat di bawah)

Setelah masuk ke panggilan, jumlah parameter yang dilewati akan ada dalam Aregister. Kode tersebut langsung kembali segera. Tidak ada konsep nilai pengembalian di Z80, hanya status masuk dan keluar. Nilai hanya "ada" yang dapat diakses dalam register karena kode tidak mengubah kondisi input, kecuali untuk PC(penghitung program) dan SP(penumpukan tumpukan). Namun, nilai dalam Atidak dapat diakses oleh BASIC dan akan ditimpa segera.

Contoh:

CALL &8000, "Hello", "World"

A = 2

CALL &8000, 42

A = 1

CALL &8000

A = 0


Berdasarkan permintaan, berikut adalah beberapa kode yang membuat nilai dapat diakses di BASIC. Saya sangat terkejut menemukan ini bisa dilakukan hanya dalam 5 byte !:

Kode mesin:

12          : LD   (DE), A
13          : INC  DE
AF          : XOR  A
12          : LD   (DE), A
C9          : RET

Saat masuk:

  • AF - register akumulator dan flag (diperlakukan sebagai dua register 8-bit)
    • A berisi jumlah parameter yang dilewati, hingga maksimal 32 parameter
    • Saya tidak yakin apa yang ada di dalamnya F. Tampaknya memiliki semua flag RESET 0, kecuali dua flag yang tidak ditentukan yang keduanya 1. The Zflag (nol) adalah SET untuk 1jika tidak ada parameter yang dikirimkan di
  • BC
    • B- 32 minus jumlah parameter ( A+ B= 32)
    • C - &FF
  • DE - Alamat parameter terakhir, atau alamat panggilan jika tidak ada parameter yang diteruskan
  • HL - Alamat byte pertama setelah perintah BASIC terpatok saat ini sedang dieksekusi (baik sebagai program atau dalam mode perintah langsung)
  • IX - Alamat stack pointer ke parameter terakhir
  • IY - &0000

Kode

  1. Loa Dalamat yang ditunjuk oleh DEdengan nilai diA
  2. INCrements DE
  3. XORs A(with A), memberi&00
  4. LApakah Dnilai Ake alamat yang ditunjukkan olehDE
  5. RETguci

Saat keluar:

  • Adihancurkan (selalu &00)
  • DE dihancurkan (selalu lebih tinggi daripada saat masuk)
  • Semua register lain dipertahankan

Dasar

Amstrad basic hanya memiliki tiga tipe data, ditambah array sederhana. Secara default, semua variabel BASIC adalah NYATA (ditandatangani, 32 bit mantissa, 8 bit eksponen), yang dapat dibuat eksplisit dengan !. Untuk penggunaan INTEGER (ditandatangani, 16 bit) %dan untuk STRING (panjang string 1 byte, hingga 255 byte data karakter, biner safe) gunakan $:

  • x - NYATA (implisit)
  • x! - NYATA (eksplisit)
  • x% - INTEGER
  • x$ - STRING

Anda juga dapat menggunakan DEFINT, DEFREALdan DEFSTRdengan satu huruf, atau rentang dua huruf tunggal untuk menentukan jenis default untuk semua variabel yang dimulai dengan huruf itu, mirip dengan FORTRAN.

  • DEFSTR a
  • DEFINT x-z

Sekarang:

  • a - STRING (implisit)
  • i - NYATA (implisit)
  • x - INTEGER (implisit)
  • x$ - STRING (eksplisit)

Jenis yang paling mudah digunakan adalah integer. Kode mesin mengharapkan parameter terakhir untuk dilewati oleh alamat, bukan nilai, yang mengapa @diawali dengan variabel. Variabel kembali dihitung sebagai salah satu CALLparameter s.

Kode mesin disebut sebagai berikut dari BASIC (dengan asumsi itu dimuat di dalam memori di alamat &8000):

CALL &8000, "Hello", "World", 42, @n%

n% = 4

Ini akan selalu memberikan hasil yang benar, terlepas dari nilai awal n%.

Untuk versi 2-byte yang mempertahankan semua register input:

CALL &8003, "Hello", "World", 42, @n%

n% = 4

Ini melompati tiga byte pertama, dan hanya memberikan hasil yang benar jika nilai awal n%adalah 0- 255. Ini berfungsi karena Z80 adalah little-endian.

Parameter pengembalian harus diinisialisasi sebelum dikirimkan, jika tidak, BASIC akan melempar Improper argumentkesalahan. Pada gambar di bawah ini, saya mencetak (dengan pintasan ?karena saya juga ikut golf demonstrasi!) Nilai kembali segera sebelum dan sesudah panggilan untuk menunjukkan perubahan nilai. Saya menggunakan nilai &FFFFkarena itu adalah representasi biner -1untuk integer yang ditandatangani. Ini menunjukkan bahwa program 5-byte dengan benar menulis kedua byte, sedangkan program 2-byte hanya menulis byte rendah dan mengasumsikan bahwa byte tinggi sudah ada &00.

masukkan deskripsi gambar di sini


Jadi, bagaimana konvensi pemanggilan yang Anda gunakan mengembalikan nilai? Jika tidak mengembalikannya di akumulator, atau sama sekali, maka jawaban Anda pada dasarnya adalah menciptakan konvensi panggilan kustom yang memecahkan masalah untuk Anda (dengan menambahkan register kembali, alih-alih melewati sebuah pointer di mana Anda dapat menyimpan A, jika itu bagaimana Anda bisa melakukannya dari BASIC). Bukannya ada yang salah dengan itu, tapi mungkin itu jawaban yang lebih menarik untuk mengikuti konvensi pemanggil yang ada.
Peter Cordes

@PeterCordes Baik Amstrad BASIC maupun Z80 tidak memiliki konsep cakupan. Semua nilai bersifat global dan langsung dapat diakses hingga hancur. Nilai Aini sama segera setelah RETinstruksi. Umur nilai dalam Asangat singkat karena akumulatornya. Tidak ada yang namanya x = CALL &8000, 42. Itu harus CALL &8000, x, 42, dan kode Z80 tambahan, tetapi kemudian xakan menjadi 2, tidak 1.
CJ Dennis

Saya pikir tidak apa-apa jika Anda memasukkan output arg dalam hitungan, jika tidak ada instruksi pengurangan 1 byte tidak ada? Saya akan tertarik untuk melihat versi yang sebenarnya dapat digunakan dari BASIC alih-alih sepele.
Peter Cordes

1
@PeterCordes Selesai! Oh, ngomong-ngomong, saya lupa menyebutkan untuk tidak memanggilnya tanpa parameter karena akan menimpa dua instruksi pertama dengan &00s - NOPno-ops. Byte lain dapat ditambahkan untuk membuatnya lebih aman, tetapi tentu saja tanpa parameter pengembalian tidak dapat mengatur apa pun.
CJ Dennis

32

Java (JDK 10) , 11 byte

a->a.length

Cobalah online!


29
Java beating Javscript jarang terlihat untuk diperhatikan
The random guy

3
@Therandomguy ini memerlukan sesuatu yang ingin interface x{void f(Object...a);}didefinisikan, dan lambda ini harus disimpan dalam variabel jenis antarmuka itu, atau diteruskan ke metode yang mengharapkan jenis antarmuka itu, jadi saya tidak benar-benar yakin itu diperhitungkan untuk tantangan ini (bahkan meskipun biasanya java lambdas diizinkan dalam tantangan codegolf)
SamYonnou

3
@ SamYonnou Tidak ada perbedaan dengan lambda lainnya, dan seperti yang Anda sebutkan, lambdas baik-baik saja .
Olivier Grégoire

@ OlivierGrégoire Saya tahu bahwa lambdas diperbolehkan, maksud saya adalah bahwa dibandingkan dengan JavaScript misalnya, Anda memerlukan lebih banyak kode tambahan untuk mengaturnya, bahkan jika Anda menggunakan sesuatu seperti REPL dan menghindari kebutuhan akan kelas / metode utama ( kebutuhan untuk antarmuka yang akan didefinisikan adalah apa yang membedakannya dari JavaScript)
SamYonnou

@ OlivierGrégoire: Saya tahu beberapa Java tetapi belum mengikutinya sama sekali. Saya tertarik untuk melihat komentar Sam tentang apa boilerplate tersapu di bawah karpet di jawaban Java yang memungkinkan itu sebenarnya sangat singkat. Saya setuju itu harus diizinkan (meskipun itu memberi Anda sesuatu yang biasanya tidak Anda dapatkan dengan fungsi Java, benar, jadi bukan hanya pengurangan boilerplate yang memberi Anda arg-menghitung built-in). Selain itu, itu masih menarik sebagai balasan untuk "Java beating JS".
Peter Cordes

25

JavaScript, 15 byte

[].push.bind(0)

The Array.prototype.pushfungsi mengambil sejumlah argumen, menambahkan mereka ke array nya, dan mengembalikan ukuran dari array. Oleh karena itu, pushfungsi yang digunakan pada array kosong mengembalikan jumlah argumen yang disediakan push.

f = [].push.bind(0)

f(10,2,65,7)
> 4

f()
> 0

The .bind(0)hanya memberikan pushfungsi tetap thisnilai sehingga dapat disimpan dalam variabel. Bahkan, pengidentifikasi 7-byte [].pushdapat digunakan secara harfiah (tetapi tidak ditugaskan) tanpa bind:

[].push(10,2,65,7)
> 4

[].push()
> 0

19

JavaScript (ES6), 16 byte

(...a)=>a.length


18

Haskell , 108 107 95 94 byte

class T r where z::Int->r
instance T Int where z=id
instance T r=>T(a->r)where z n _=z$n+1
z 0

Cobalah online!

Ini mengejutkan sulit untuk bekerja, tetapi saya senang mencoba mencari tahu bagaimana menerapkan sesuatu yang sepele dalam bahasa imperatif.


Sial, kamu mengalahkanku. fopsional jika Anda katakan z 0adalah fungsi tanpa penjilidan, maka main = print $ ((z 0) pi 0 () [] :: Int)berfungsi.
Angs

Dan maksud saya bahwa tipe-tipe itu bekerja ketika digunakan sebagai fungsi anonim sehingga Anda memang dapat menghapus semuanya dari dua baris terakhir kecualiz 0
Angs

Terima kasih banyak! Ternyata saya melakukan sesuatu yang salah ketika saya menguji fungsi anonim. Mencoba contoh Anda dan itu berhasil dengan baik.
user9549915

Saya kira ::Intharus dihitung dalam hitungan byte, karena jenis jawaban harus dinyatakan cepat atau lambat, seperti pada main = print $ ((z 0 :: Double -> Integer -> () -> [a] -> (Int->Int->Int) -> IO () -> Int) pi 0 () [] (+) main). Saya juga berpikir bahwa ini hanya berfungsi selama waktu kompilasi sehingga sesuatu seperti foldl(\a b->a b) (z 0) $ [1..5])::Inttidak dapat bekerja. Apa pun itu, ini adalah hal yang hebat.
Angs

2
s/imperative/non-curry/
user202729


12

Zsh , 7 5 byte

<<<$#

Cobalah online!


Meskipun mungkin harus dibungkus:f(){ echo $#; }
muru

8
@uru Itu terlihat bagus sebagai program lengkap untuk saya.
Neil

Meskipun, saya melihat sekarang bahwa OP hanya menginginkan fungsi ...
Neil

2
Skrip shell @Neil bertindak persis seperti fungsi. OP tidak jelas tentang apa fungsi itu, saya mengklaim kiriman saya hanya fungsi yang disimpan ke disk.
Pavel

9

Brain-Flak , 6 byte

Solusi Brain-Flak pertama saya yang layak diposkan, saya kira itu alat yang tepat untuk pekerjaan ini:

([]<>)

Cobalah online!

Penjelasan

Saat menjalankan program Brain-Flak, pada awalnya tumpukan kiri berisi semua argumen. Dari sana itu hanya masalah:

(      -- push the following..
 []    --   height of the stack (ie. # of arguments)
   <>  -- ..to the other stack  (toggles to the other stack)
)      --
       -- the right stack now contains the # of arguments which
       -- gets printed implicitly

7

Bahasa Wolfram (Mathematica) , 11 byte

Tr[1^{##}]&

Cobalah online!

Disarankan oleh JungHwan Min. Beberapa batasan (input harus berbentuk persegi panjang) tetapi kami tidak diharuskan untuk menangani input yang sewenang-wenang.

11 byte

Length@!##&

Cobalah online!

Solusi 11 byte lain yang disarankan oleh Martin Ender. Ini tampaknya salah ketika tidak ada satu input tetapi masih mengembalikan nilai yang benar dalam semua kasus.

12 byte

Length@{##}&

Cobalah online!

Solusi asli saya

Dalam Mathematica ##singkatan dari sejumlah argumen dalam suatu fungsi. {dan }membungkusnya dalam daftar dan Length@mengambil panjang daftar ini. &pada akhirnya mengubahnya menjadi fungsi yang sebenarnya.


7

R , 30 byte

function(...)length(list(...))

Cobalah online!


1
function(...)nargs()adalah 20 byte, tetapi menggunakan length(...)pendekatan awal saya sampai saya googled nargsfungsi-like.
Giuseppe

@ Giuseppe hmm, saya mencoba mengonversikannya list(...)menjadi logis sehingga sum()dapat digunakan, tapi itu rumit: /
JAD

1
Haha, jangan coba-coba dan memancing saya ke argumen itu;)
RoryT

1
@RoryT oh sebenarnya, R docs bilang gabung. Nevermind: D
JAD

2
...length() melakukan hal yang sama denganlength(list(...))
Giuseppe

7

Bash, 12 byte (terima kasih kepada paxdiablo karena menyimpan 4)

n()(echo $#)

Salin dan tempel di bash prompt. Kemudian jalankan fungsi n dari prompt:

$ n
0
$ n 46 gr 3443 dad
4
$ n 4fwj23 wrw jdwj 00998 34 eyt q3 vg wq j qw
11

2
Selamat datang di PPCG!
Martin Ender

dapatkah Anda mengatakan itu adalah skrip "./n" dan bukan sebuah fungsi? maka itu hanya echo $#:, 7 byte. (nanti akan ada shell yang Anda gunakan untuk meluncurkan skrip "./n" dengan. yaitu, Anda menjalankan bash? lalu ketika Anda: ./n arg1 ... argnitu akan ditafsirkan oleh bash.)
Olivier Dulac

@Olivier Dulac Tantangannya jelas mengatakan suatu fungsi.
Wastrel

7

C ++ 14 (gcc) , 34 byte

Sebagai fungsi lambda variadik generik (diperlukan C ++ 14):

[](auto...p){return sizeof...(p);}

Cobalah online!

Sebelumnya (salah) menjawab: 32 byte

Itu hilang template<class...T>dan(p)

int f(T...p){return sizeof...p;}

6
C ++ 14, C ++ 11 tidak memiliki lambda generik.
Quentin


@ nwp: tidak akan -fpermissivedikenakan biaya 12 byte untuk opsi itu, sih ? Jika bukan standar ISO C ++ atau GNU C ++.
Peter Cordes

@PeterCordes Mungkin dan memang dimaksudkan untuk menghindari memiliki solusi 0-byte sepele untuk semuanya dengan melewati program melalui baris perintah. Saya hanya tidak memikirkan hal itu di sini karena sepertinya tidak kasar.
nwp

@ Quentin diperbaiki -> C ++ 14
Bierpfurz


5

Oktaf , 9 byte

@()nargin

Cobalah online!

Anonymous function taking any number of arguments (and silently discarding the lot), and outputs the number of arguments through the built-in nargin. This does not work in MATLAB, where you would need varargin to allow for arbitrary many arguments.



4

Perl 5, 9 bytes

sub{~~@_}

Try it online!


A quick sitewide search of answers seems to indicate that you can leave out the sub
ASCII-only

2
Protip: TIO let's you copy in PPCG post format (ESC, S, G)
ASCII-only

@ASCII-only Oh nice, thanks! :) As for leaving out the sub, I don't think so. It's not a function without it.
Chris

@ASCII-only I'd consider answers without sub invalid since the result isn't something you can call or assign to a variable
Ton Hospel


4

C# .NET, 11 bytes

a=>a.Length

Try it online.

Explanation:

In C# .NET object is used for multi-type arguments, allowing one to pass integers, strings, characters, etc. as possible inputs. For example:

// Can be called like: `F(2)`, `F("test")`, `F('a')`, etc.
void F(object arg){ ... }

C# .NET can also have a fixed size of optional arguments. For example:

// Can be called like: `F()`, `F(2)`, `F("test")`, `F('a')`, etc.
void F(object arg = null){ ... }

And there are also varargs, which is an undefined amount of optional arguments (which is what I've used in this answer). For example:

// Can be called like: `F()`, `F(2)`, `F(2, "test", 'a')`, etc.
void F(params object[] args){ ... }

Usually lambdas are created like this:

System.Func<object[], int> F f = a=>a.Length;
// A call like `f(new object[]{2, "test", 'a'))` will return 3 (size of the input array)

But unfortunately System.Func doesn't support params varargs, so I'll have to create a delegate instead:

delegate int F(params object[] args);
F f = a=>a.Length;
// A call like `f()` will return 0, and `f(2, "test", 'a')` will return 3

Which is my answer for this challenge, and can be found in the linked TIO test code.


The only limitation is that inputting an actual object[] like f(new object[]{1,2,3}) will result in 3 instead of 1. f(new int[]{1,2,3}) will still result in 1, because it interprets the int[] as a single object. To have the object[] parameter be interpret as a single object as well it can be casted to an object like this: f((object)new object[]{1,2,3}).


I have to say, if there were ever an answer that made me support including lambda-related boilerplate in C# answers it would be this one... but it is definitely a valid solution.
Kamil Drakari

@KamilDrakari Maybe it indeed wasn't very clear what I did without opening the TIO-link, so I've added an explanation.
Kevin Cruijssen

1
@Taemyr I tried finding a solution, but unfortunately there is none for C# .NET, except for casting any object[] parameters to object, like this: f((object)new object[]{1,2,3});. There is no way to differentiate between f(new object[]{1,2,3}); and f(1,2,3); as far as I could find.
Kevin Cruijssen

1
this handles array parameters correctly for a huge penalty of bytes. There might be a more concise structure that can handle it, but it works in my testing.
Kamil Drakari

1
@KamilDrakari Hmm, but it fails for f(1, new object[]{1,2,3}) again though. Not sure if a solution for this behavior can be found.
Kevin Cruijssen

4

Dodos, 32 31 bytes

f
	dot i f dab
i
	
	dip dot dab

Try it online!

Uses Dennis' increment function.

Explanation

f                     # definition of f - target function
        dot i f dab   # sum of j(f(all args but first)). recurses until it has 0 args
i                     # definition of i - returns (arg, 1) given 1 arg
                      # arg
        dip dot dab   # 1 (dot dab on list of length 1 returns 0, dip returns |0 - 1|)

Alternatively, 32 bytes without recursion in target function (thanks @Leo)

	dot i
i
	dip dot dab dot
	i dab

Try it online!

Explanation

        dot i             # anonymous function: sum of i(args)
                          # here this becomes implicit main
i                         # definition of i - returns a list with all arguments replaced with 1
        dip dot dab dot   # 1 (dab dot returns empty list, dot returns 0, dip returns |0 - 1|
        i dab             # list concatenated with i(all args but first)

Here's another same-length solution Try it online! I can't seem to understand why yours works though, could you add an explanation please?
Leo

Hey, you added an explanation to my solution! I wanted one for yours, I know how mine works xD
Leo

1
@Leo sorry for late reply, idek what I'm doing, just copied Dennis' function, will try to understand asap. I had no idea how dodos works so I figured out what yours did first
ASCII-only

No worries, it was just a funny situation :)
Leo

@Leo ok so does my explanation make sense? (note: I'm on mobile so feel free to edit it to make it better lol)
ASCII-only

3

C++, 72 bytes

int f(){return 0;}template<class...P>int f(int,P...p){return f(p...)+1;}

Saves bytes by only working with ints.


You can use sizeof....
L. F.

3

Rust, 57 bytes

macro_rules!f{()=>{0};($($x:expr),+)=>{[$($x),+].len()};}

Explanation:

macro_rules! f {         // define a macro called f
    () => {0};           // when called without arguments, expand to 0
    ($($x:expr),+) => {  // when called with 1 or more comma seperated arguments
        [                // rust uses [a, b, c] to make an array
            $($x),+      // expand to the arguments seperated with a comma
        ]                
        .len()           // take the length of that.
    };
}

Test:

fn main() {
    println!("{:?}", f!());                // prints 0
    println!("{:?}", f!(4));               // prints 1
    println!("{:?}", f!(5, 2));            // prints 2
    // works with anything, as long as you dont mix things
    println!("{}", f!("", "a", "hello"));  // prints 3
}




2

PHP, 11 bytes

<?=$argc-1;

Try it online: 1 input | 3 inputs


I'm not so sure about this one (and it's validity) since it is the count of arguments passed to call PHP.
Ismael Miguel

@IsmaelMiguel, see this consensus.
Shaggy

1
The question explicitly requires a function that returns the number, does not display it: "...write a function that takes a variable number of arguments and returns the number of arguments."
axiac

1
Re-quoting the question: "Using your language of choice, write a function that takes a variable number of arguments and returns the number of arguments it was called with.". Your code doesn't contain functions.
Ismael Miguel

@IsmaelMiguel, if that were indeed the case then many other solutions would also be invalidated. The norm is to allow solutions to be programmes or functions.
Shaggy

2

Batch, 50 49 bytes

set n=0
for %%a in (%*)do set/an+=1
exit/b%n%

No builtin in Batch, so we have to go old-school. Saved 1 byte thanks to @IsmaelMiguel. Outputs via exit code, or save 3 bytes if output via global variable is valid. Example of use in a full program:

@echo off
call:c %*
echo %ERRORLEVEL%
exit/b
:c
set n=0
for %%a in (%*)do set/an+=1
exit/b%n%

I believe that this answer is answer goes (somewhat) against the rules. Batch has something somewhat close to functions. You can do something similar to :a|set r=0&for %%a in (%*)do set/ar+=1 (| = windows-style newline). This solution is 38 bytes. To execute it, do call :a <args> with a goto :eof before the function, being the value available inside the variable r. If you want to keep your solution, remove the /a on the first set, and remove those @.
Ismael Miguel

@IsmaelMiguel Like this? (Note: I didn't include the function name in the byte count, but I did include the function return, which seems reasonable, as there needs to be one somewhere.)
Neil

Yes, that's exactly it. Nice catch with the exit code! I was surprised to see that exitcodes can be larger than 255. An example is the list provided by Symantec: symantec.com/connect/articles/…
Ismael Miguel

2

x86 32-bit (i386) machine code function, 13 bytes

Calling convention: i386 System V (stack args), with a NULL pointer as a sentinel / terminator for the end-of-arg-list. (Clobbers EDI, otherwise complies with SysV).

C (and asm) don't pass type info to variadic functions, so the OP's description of passing integers or arrays with no explicit type info could only be implemented in a convention that passed some kind of struct / class object (or pointers to such), not bare integers on the stack. So I decided to assume that all the args were non-NULL pointers, and the caller passes a NULL terminator.

A NULL-terminated pointer list of args is actually used in C for functions like POSIX execl(3): int execl(const char *path, const char *arg, ... /* (char *) NULL */);

C doesn't allow int foo(...); prototypes with no fixed arg, but int foo(); means the same thing: args unspecified. (Unlike in C++ where it means int foo(void)). In any case, this is an asm answer. Coaxing a C compiler to call this function directly is interesting but not required.

nasm -felf32 -l/dev/stdout arg-count.asm with some comment lines removed.

24                       global argcount_pointer_loop
25                       argcount_pointer_loop:
26                               .entry:
28 00000000 31C0             xor   eax, eax  ; search pattern = NULL
29 00000002 99               cdq             ; counter = 0
30 00000003 89E7             mov   edi, esp
31                       ;    scasd           ; edi+=4; skip retaddr
32                       .scan_args:
33 00000005 42               inc   edx
34 00000006 AF               scasd            ; cmp eax,[edi] / edi+=4
35 00000007 75FC             jne  .scan_args
36                       ;    dec   edx       ; correct for overshoot: don't count terminator
37                       ;    xchg  eax,edx
38 00000009 8D42FE           lea   eax, [edx-2]    ; terminator + ret addr
40 0000000C C3               ret

size = 0D               db $ - .entry

The question shows that the function must be able to return 0, and I decided to follow that requirement by not including the terminating NULL pointer in the arg count. This does cost 1 byte, though. (For the 12-byte version, remove the LEA and uncomment the scasd outside the loop and the xchg, but not the dec edx. I used LEA because it costs the same as those other three instructions put together, but is more efficient, so the function is fewer uops.)

C caller for testing:

Built with:

nasm -felf32 -l /dev/stdout arg-count.asm | cut -b -28,$((28+12))- &&
 gcc -Wall -O3 -g -std=gnu11 -m32 -fcall-used-edi arg-count.c arg-count.o -o ac &&
 ./ac

-fcall-used-edi is required even at -O0 to tell gcc to assume that functions clobber edi without saving/restoring it, because I used so many calls in one C statement (the printf call) that even -O0 was using EDI. It appears to be safe for gcc's main to clobber EDI from its own caller (in CRT code), on Linux with glibc, but otherwise it's totally bogus to mix/match code compiled with different -fcall-used-reg. There's no __attribute__ version of it to let us declare the asm functions with custom calling conventions different from the usual.

#include <stdio.h>

int argcount_rep_scas();       // not (...): ISO C requires at least one fixed arg
int argcount_pointer_loop();   // if you declare args at all
int argcount_loopne();

#define TEST(...) printf("count=%d = %d = %d   (scasd/jne) | (rep scas) | (scas/loopne)\n", \
        argcount_pointer_loop(__VA_ARGS__), argcount_rep_scas(__VA_ARGS__), \
        argcount_loopne(__VA_ARGS__))

int main(void) {
    TEST("abc", 0);
    TEST(1, 1, 1, 1, 1, 1, 1, 0);
    TEST(0);
}

Two other versions also came in at 13 bytes: this one based on loopne returns a value that's too high by 1.

45                       global argcount_loopne
46                       argcount_loopne:
47                           .entry:
49 00000010 31C0             xor   eax, eax  ; search pattern = NULL
50 00000012 31C9             xor   ecx, ecx  ; counter = 0
51 00000014 89E7             mov   edi, esp
52 00000016 AF               scasd           ; edi+=4; skip retaddr
53                       .scan_args:
54 00000017 AF               scasd
55 00000018 E0FD             loopne  .scan_args
56 0000001A 29C8             sub   eax, ecx
58 0000001C C3               ret

size = 0D = 13 bytes               db $ - .entry

This version uses rep scasd instead of a loop, but takes the arg count modulo 256. (Or capped at 256 if the upper bytes of ecx are 0 on entry!)

63                       ; return int8_t maybe?
64                       global argcount_rep_scas
65                       argcount_rep_scas:
66                               .entry:
67 00000020 31C0             xor   eax, eax
68                           ;    lea   ecx, [eax-1]
69 00000022 B1FF             mov   cl, -1
70 00000024 89E7             mov   edi, esp
71                       ;    scasd              ; skip retaddr
72 00000026 F2AF             repne scasd        ; ecx = -len - 2 (including retaddr)
73 00000028 B0FD             mov   al, -3
74 0000002A 28C8             sub   al, cl       ; eax = -3 +len + 2
75                       ;    dec   eax
76                       ;    dec   eax
77 0000002C C3               ret

size =  0D = 13 bytes         db $ - .entry

Amusingly, yet another version based on inc eax / pop edx / test edx,edx / jnz came in at 13 bytes. It's a callee-pops convention, which is never used by C implementations for variadic functions. (I popped the ret addr into ecx, and jmp ecx instead of ret. (Or push/ret to not break the return-address predictor stack).



1

JavaScript, 35 bytes

f=
function(){return arguments.length}

console.log(f(2,5,4))


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.