Основное преимущество реальных выходных параметров — прямое изменение одной или нескольких скалярных переменных в области действия вызывающего объекта. Среди подходов, предложенных в других ответах, этому требованию удовлетворяют только обратные вызовы:
function tryparse_int_1(s, cb)
{ var res = parseInt(s);
cb(res);
return !isNaN( res );
}
function test_1(s)
{ var /* inreger */ i;
if( tryparse_int_1( s, x=>i=x ) )
console.log(`String "${s}" is parsed as integer ${i}.`); else
console.log(`String "${s}" does not start with an integer.`);
}
test_1("47");
test_1("forty-seven");
В этом случае для передачи каждого выходного параметра требуется пять дополнительных символов, чтобы обернуть его идентификатор в анонимную функцию установки. Он не очень удобочитаем и часто неудобен для ввода, поэтому можно прибегнуть к единственному наиболее интересному свойству языков сценариев — их способности творить чудеса, например, выполнять строки как код.
В следующем примере реализована расширенная версия вышеприведенной функции синтаксического анализа целых чисел, которая теперь имеет два выходных параметра: результирующее целое число и флаг, указывающий, является ли оно положительным:
/* ------------ General emulator of output parameters ------------ */
function out_lit(v)
{ var res;
if( typeof(v) === "string" )
res = '"' + v.split('\"').join('\\\"') + '"'; else
res = `${v}`;
return res;
}
function out_setpar(col, name, value)
{ if( col.outs == undefined ) col.outs = [];
col.outs[name] = value;
}
function out_setret(col, value)
{ col.ret = value; }
function out_ret( col )
{ var s;
for(e in col.outs)
{ s = s + "," + e + "=" + out_lit( col.outs[e] ); }
if( col.ret != undefined )
{ s = s + "," + out_lit( col.ret ); }
return s;
}
/* -------- An intger-parsing function using the emulator -------- */
function tryparse_int_2 // parse the prefix of a string as an integer
( /* string */ s, // in: input string
/* integer */ int, // out: parsed integer value
/* boolean */ pos // out: whether the result is positive
)
{ var /* integer */ res; // function result
var /* array */ col; // collection of out parameters
res = parseInt(s);
col = [];
out_setpar( col, int, res );
out_setpar( col, pos, res > 0 );
out_setret( col, !isNaN( res ) );
return out_ret( col );
}
В этой версии для передачи каждого выходного параметра требуется два дополнительных символа вокруг его идентификатора, чтобы встроить его в строковый литерал, плюс шесть символов на вызов для оценки результата:
function test_2(s)
{ var /* integer */ int;
var /* boolean */ pos;
if( !eval( tryparse_int_2( s, "int", "pos" ) ) )
{ console.log(`String "${s}" does not start with an integer.`); }
else
{ if( pos ) adj = "positive";
else adj = "non-positive";
console.log(`String "${s}" is parsed as a ${adj} integer ${int}.`);
}
}
test_2( "55 parrots" );
test_2( "-7 thoughts" );
test_2( "several balls" );
Вывод тестового кода выше:
String "55 parrots" is parsed as a positive integer 55.
String "-7 thoughts" is parsed as a non-positive integer -7.
String "several balls" does not start with an integer.
Однако у этого решения есть недостаток: оно не может обрабатывать возвраты небазовых типов.
Возможно, более чистым подходом является эмуляция указателей:
// Returns JavaScript for the defintion of a "pointer" to a variable named `v':
// The identifier of the pointer is that of the variable prepended by a $.
function makeref(v)
{ return `var $${v} = {set _(val){${v}=val;},get _() {return ${v};}}`; }
// Calcualtes the square root of `value` and puts it into `$root`.
// Returns whether the operation has succeeded.
// In case of an error, stores error message in `$errmsg`.
function sqrt2
( /* in number */ value, /* value to take the root of */
/* out number */ $root , /* "pointer" to result */
/* out string */ $errmsg /* "pointer" to error message */
)
{ if( typeof( value ) !== "number" )
{ $errmsg._ = "value is not a number.";
return false;
}
if( value < 0 )
{ $errmsg._ = "value is negative.";
return false;
}
$root._ = Math.sqrt(value);
return true;
}
Следующий тестовый код:
function test(v)
{ var /* string */ resmsg;
var /* number */ root ; eval( makeref( "root" ) );
var /* string */ errmsg; eval( makeref( "errmsg" ) );
if( sqrt2(v, $root, $errmsg) ) resmsg = `Success: ${root}`;
else resmsg = `Error: ${errmsg}`;
console.log(`Square root of ${v}: ` + resmsg );
}
test("s" );
test(-5 );
test( 1.44);
печатает:
Square root of s: Error: value is not a number.
Square root of -5: Error: value is negative.
Square root of 1.44: Success: 1.2
Указатели, созданные этим методом, можно повторно использовать в других функциях и последующих вызовах той же функции. Например, вы можете определить функцию, которая добавляет строки:
// Append string `sep' to a string pointed to by $s, using `sep` as separator:
// $s shall not point to an undefined value.
function append($s, sep, val)
{ if( $s._ != '' ) $s._ += sep;
$s._ += val;
}
и используйте его таким образом:
const sep = ", "
var s; eval( makeref("s") );
s = '';
append( $s, sep, "one" );
append( $s, sep, "two" );
append( $s, sep, "three" );
console.log( s );
Он будет печатать:
one, two, three
person
Ant_222
schedule
14.03.2021