I'm trying to create an if-else statement in Lua script. But I can't really get it to work :( This is what I have.
First I have a randomizer that can choose between some plans
local phones_priceplan = {
{ priceplan = 'Fast'},
{ priceplan = 'Mini'},
{ priceplan = 'kort'},
}
local rnd_priceplan = math.random(1, #phones_priceplan)
local priceplan = phones_priceplan[rnd_priceplan]['priceplan']
Then if the priceplan is equal to 'Fast' I want to run another randomizer
if priceplan == "Fastpris"
local fastpris_plan = {
{ price = '145', gb = '0.5', },
{ price = '195', gb = '2', },
{ price = '245', gb = '6', },
}
local rnd_phone_surf_plan = math.random(1, #fastpris_plan)
local surf_price = fastpris_plan[rnd_phone_surf_plan]['surf_price']
end
But it is with the if statement that it seems so crash. Any ideas on what could possible be wrong :)?
In Lua, the syntax of an if statement is as follows:
if cond then
statement
elseif cond then
statement
else
if cond then
statement
else
statement
end
end
You're missing the then clause.
Related
I'm trying to simply compare a line in a text file to today's date.
The line I want help with always seems to evaluate true for my code.
Any examples?
My code:
set %lines $lines(test.txt)
set %date $adate
while (%i <= %lines)
set %read $read(test.txt, n, %i)
if( %date isin %read ){ ; <-- Line in question
do things
}
}
You have a few errors.
Your while loop is missing an open bracket. (And closing at the end)
while (%i <= %lines) {
You must have a space between () { } and the rest of the lines
if<space>(
)<space> {
if ( %date isin %read ) {
I took the liberty of suggesting another version.
Code:
var %filename = test.txt
var %lines = $lines(%filename)
var %currentDate = $adate
var %i = 1
while (%i <= %lines) {
var %line = $read(%filename, n, %i)
if (%currentDate isin %line) {
# do things
# Should uncomment the break in case you want to stop after a match
#break
}
inc %i
}
I am sorry if I didn't understand, but what is the reason for a complex script just to check if there is a date format in a line of a text file?
There is no reason to set a variable %read to store the line of the loop in question, when you can do an IF condition after the loop:
var %x = 1
while (%x <= $lines(test.txt)) {
if ($adate isin $read(test.txt,n,%x)) {
;do things
}
inc %x
}
Meaning, they don't have to be distributed. I'm thinking about using memcached or redis for that. Probably the latter one. What I'm concerned about is "we've got to free some memory, so we'll delete this key/value before it expired" thing. But I'm open to other suggestions as well.
tl;dr Use ready-made solution, suggested by developers.
So, I decided not to use memcached for the purpose. Since it's a caching server. I don't see a way to ensure that it doesn't delete my keys because it's out of memory. With, redis that's not an issue as long as maxmemory-policy = noeviction.
There are 3 links I want to share with you. They are basically 3 ways, that I now know, to solve the issue. As long as you have redis >= 2.6.0 that is.
redis >= 2.6.12
If you've got redis >= 2.6.12, you're lucky and can simply use setnx command with its new options ex and nx:
$redis->set($name, <whatever>, array('nx', 'ex' => $ttl));
But we can't just delete the lock in the end, if we are to allow for critical section taking longer then we expected (>= ttl). Consider the following situation:
C1 acquires the lock
lock expires
C2 acquires the lock
C1 deletes C2's lock
For that not to happen we are going to store current timestamp as a value of the lock. Then, knowing that Lua scripts are atomic (see Atomicity of scripts):
$redis->eval('
if redis.call("get", KEYS[1]) == KEYS[2] then
redis.call("del", KEYS[1])
end
', array($name, $now));
However, is it possible for two clients to have equal now values? For that all the above actions should happen within one second and ttl must be equal to 0.
Resulting code:
function get_redis() {
static $redis;
if ( ! $redis) {
$redis = new Redis;
$redis->connect('127.0.0.1');
}
return $redis;
}
function acquire_lock($name, $ttl) {
if ( ! $ttl)
return FALSE;
$redis = get_redis();
$now = time();
$r = $redis->set($name, $now, array('nx', 'ex' => $ttl));
if ( ! $r)
return FALSE;
$lock = new RedisLock($redis, $name, $now);
register_shutdown_function(function() use ($lock) {
$r = $lock->release();
# if ( ! $r) {
# Here we can log the fact that lock has expired too early
# }
});
return $lock;
}
class RedisLock {
var $redis;
var $name;
var $now;
var $released;
function __construct($redis, $name, $now) {
$this->redis = get_redis();
$this->name = $name;
$this->now = $now;
}
function release() {
if ($this->released)
return TRUE;
$r = $this->redis->eval('
if redis.call("get", KEYS[1]) == KEYS[2] then
redis.call("del", KEYS[1])
return 1
else
return 0
end
', array($this->name, $this->now));
if ($r)
$this->released = TRUE;
return $r;
}
}
$l1 = acquire_lock('l1', 4);
var_dump($l1 ? date('H:i:s', $l1->expires_at) : FALSE);
sleep(2);
$l2 = acquire_lock('l1', 4);
var_dump($l2 ? date('H:i:s', $l2->expires_at) : FALSE); # FALSE
sleep(4);
$l3 = acquire_lock('l1', 4);
var_dump($l3 ? date('H:i:s', $l3->expires_at) : FALSE);
expire
The other solution I found here. You simply make the value expire with expire command:
$redis->eval('
local r = redis.call("setnx", ARGV[1], ARGV[2])
if r == 1 then
redis.call("expire", ARGV[1], ARGV[3])
end
', array($name, $now, $ttl));
So, only acquire_lock function changes:
function acquire_lock($name, $ttl) {
if ( ! $ttl)
return FALSE;
$redis = get_redis();
$now = time();
$r = $redis->eval('
local r = redis.call("setnx", ARGV[1], ARGV[2])
if r == 1 then
redis.call("expire", ARGV[1], ARGV[3])
end
return r
', array($name, $now, $ttl));
if ( ! $r)
return FALSE;
$lock = new RedisLock($redis, $name, $now);
register_shutdown_function(function() use ($lock) {
$r = $lock->release();
# if ( ! $r) {
# Here we can log that lock as expired too early
# }
});
return $lock;
}
getset
And the last one is described again in documentation. Marked with "left for historical reasons" note.
This time we store timestamp of the moment when the lock is to expire. We store it with setnx command. If it succeeds, we've acquired the lock. Otherwise, either someone else's holding the lock, or the lock has expired. Be it the latter, we use getset to set new value and if the old value hasn't changed, we've acquired the lock:
$r = $redis->setnx($name, $expires_at);
if ( ! $r) {
$cur_expires_at = $redis->get($name);
if ($cur_expires_at > time())
return FALSE;
$cur_expires_at_2 = $redis->getset($name, $expires_at);
if ($cur_expires_at_2 != $cur_expires_at)
return FALSE;
}
What makes me uncomfortable here is that we seem to have changed someone else's expires_at value, don't we?
On a side note, you can check which redis is it that you're using this way:
function get_redis_version() {
static $redis_version;
if ( ! $redis_version) {
$redis = get_redis();
$info = $redis->info();
$redis_version = $info['redis_version'];
}
return $redis_version;
}
if (version_compare(get_redis_version(), '2.6.12') >= 0) {
...
}
Some debugging functions:
function redis_var_dump($keys) {
foreach (get_redis()->keys($keys) as $key) {
$ttl = get_redis()->ttl($key);
printf("%s: %s%s%s", $key, get_redis()->get($key),
$ttl >= 0 ? sprintf(" (ttl: %s)", $ttl) : '',
nl());
}
}
function nl() {
return PHP_SAPI == 'cli' ? "\n" : '<br>';
}
I hate to annoy you guys with this question, but I am getting the error "{ required" even though all my loops appear to be open (and closed) properly and unfortunately Stata doesn't tell you where the error is, so I can't figure out why this is happening. By the way if I take out the append_replace section with the if statements, I am still getting the same error, so I don't think it is from that section. Here is my code:
local vars = "any_rate resp_rate circ_rate weight_rate diabetes_rate gallstones_rate mental_rate cancer_rate std_rate died_rate"
local dates = "1947 1974"
foreach var of local `vars' {
foreach i of local `dates' {
forvalues j = 500(100)2500 {
local append_replace = "append"
if "`var'"=="any_rate" {
if "`i'" == "1947" {
if `j' == 500 {
local append_replace = "replace"
}
}
}
reg `var' post`i' dobdistfrom`i'change dobdistfrom`i'changesq post`i'_dist`i' post`i'_dist`i'sq if dobdistfrom`i'change < `j' & dobdistfrom`i'change > -`j', cluster(dobdistfrom`i'change)
outreg2 using Prelim_RD_Estimates.xls, `append_replace' excel dec(3)
}
}
}
Thanks so much for your help!
I believe the problem is with the local that prevents the { from being read.
Original problematic version:
local dates = "1947 1974"
foreach i of local `dates' {
di `i'
}
Corrected version:
local dates = "1947 1974"
foreach i in `dates' {
di `i'
}
You could also just omit the quotes in "foreach i of local dates" in your original construction.
I'm trying to pass context into a dynamic expression that I evaluate every iteration of a for loop. I understand that the load string only evaluates within a global context meaning local variables are inaccessible. In my case I work around this limitation by converting a local into a global for the purpose of the string evaluation. Here's what I have:
require 'cosmo'
model = { { player = "Cliff", age = 35, gender = "male" }, { player = "Ally", age = 36, gender = "female" }, { player = "Jasmine", age = 13, gender = "female" }, { player = "Lauren", age = 6.5, gender = "female" } }
values = { eval = function(args)
output = ''
condition = assert(loadstring('return ' .. args.condition))
for _, it in ipairs(model) do
each = it
if condition() then
output = output .. each.player .. ' age: ' .. each.age .. ' ' .. '\n'
end
end
return output
end }
template = "$eval{ condition = 'each.age < 30' }"
result = cosmo.fill(template, values)
print (result)
My ultimate goal (other than mastering Lua) is to build out an XSLT like tempting engine where I could do something like:
apply_templates{ match = each.age > 30}[[<parent-player>$each.player</parent-player>]]
apply_templates{ match = each.age > 30}[[<child-player>$each.player</child-player>]]
...And generate different outputs. Currently I'm stuck on my above hawkish means of sharing a local context thru a global. Does anyone here have better insight on how I'd go about doing what I'm attempting to do?
It's worth noting that setfenv was removed from Lua 5.2 and loadstring is deprecated. 5.2 is pretty new so you won't have to worry about it for a while, but it is possible to write a load routine that works for both versions:
local function load_code(code, environment)
if setfenv and loadstring then
local f = assert(loadstring(code))
setfenv(f,environment)
return f
else
return assert(load(code, nil,"t",environment))
end
end
local context = {}
context.string = string
context.table = table
-- etc. add libraries/functions that are safe for your application.
-- see: http://lua-users.org/wiki/SandBoxes
local condition = load_code("return " .. args.condition, context)
Version 5.2's load handles both the old loadstring behavior and sets the environment (context, in your example). Version 5.2 also changes the concept of environments, so loadstring may be the least of your worries. Still, it's something to consider to possibly save yourself some work down the road.
You can change the context of a function with setfenv(). This allows you to basically sandbox the loaded function into its own private environment. Something like the following should work:
local context = {}
local condition = assert(loadstring('return ' .. args.condition))
setfenv(condition, context)
for _, it in ipairs(model) do
context['each'] = it
if condition() then
-- ...
This will also prevent the condition value from being able to access any data you don't want it to, or more crucially, modifying any data you don't want it to. Note, however, that you'll need to expose any top-level bindings into the context table that you want condition to be able to access (e.g. if you want it to have access to the math package then you'll need to stick that into context). Alternatively, if you don't have any problem with condition having global access and you simply want to deal with not making your local a global, you can use a metatable on context to have it pass unknown indexes through to _G:
setmetatable(context, { __index = _G })
$q = $this->_em->createQuery("SELECT s FROM app\models\Quest s
LEFT JOIN s.que c
WHERE s.type = '$sub'
AND c.id = '$id'");
Given a query like the one above, how would I retrieve the number of results?
Alternatively one can look at what Doctrine Paginator class does to a Query object to get a count (this aproach is most probably an overkill though, but it answers your question):
public function count()
{
if ($this->count === null) {
/* #var $countQuery Query */
$countQuery = $this->cloneQuery($this->query);
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
}
if ($this->useOutputWalker($countQuery)) {
$platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win
$rsm = new ResultSetMapping();
$rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count');
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker');
$countQuery->setResultSetMapping($rsm);
} else {
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
}
$countQuery->setFirstResult(null)->setMaxResults(null);
try {
$data = $countQuery->getScalarResult();
$data = array_map('current', $data);
$this->count = array_sum($data);
} catch(NoResultException $e) {
$this->count = 0;
}
}
return $this->count;
}
You can either perform a count query beforehand:
$count = $em->createQuery('SELECT count(s) FROM app\models\Quest s
LEFT JOIN s.que c
WHERE s.type=:type
AND c.id=:id)
->setParameter('type', $sub);
->setParameter('id', $id);
->getSingleScalarResult();
Or you can just execute your query and get the size of the results array:
$quests = $q->getResult();
$count = count($quests);
Use the first method if you need the count so that you can make a decision before actually retrieving the objects.