Gadfly has a nifty macro that generates a copy constructor and optional argument constructor for some large types:
# Generate large types where each field has a default value to which it's
# initialized.
macro varset(name::Symbol, table)
#assert table.head == :block
table = table.args
names = Any[]
vars = Any[]
parameters = Any[]
parameters_expr = Expr(:parameters)
for row in table
if isa(row, Expr) && row.head == :line
continue
end
if isa(row, Symbol)
var = row
typ = :Any
default = :nothing
elseif row.head == :tuple
#assert 2 <= length(row.args) <= 3
var = row.args[1]
typ = row.args[2]
default = length(row.args) > 2 ? row.args[3] : :nothing
else
error("Bad varset systax")
end
push!(names, var)
push!(vars, :($(var)::$(typ)))
push!(parameters, Expr(:kw, var, default))
parameters_expr = Expr(:parameters, parameters...)
end
new_with_defaults = Expr(:call, :new, names...)
ex =
quote
type $(name)
$(vars...)
function $(name)($(parameters_expr))
$(new_with_defaults)
end
# shallow copy constructor
function $(name)(a::$(name))
b = new()
for name in fieldnames($(name))
setfield!(b, name, getfield(a, name))
end
b
end
end
function copy(a::$(name))
$(name)(a)
end
end
esc(ex)
end
I would like to add a partial copy constructor that lets you provide an object and some partial parameters. It would copy the provided object and overwrite only the fields that you give it.
How could I define something like the following? Pseudocode-ish:
function $(name)(a::$(name), $(parameters_expr))
b = new(a)
for name in names
if getfield(b, name) != default
setfield!(b, name, new_val)
end
end
end
Related
So i'm trying to convert python to lua but a road block i have right now is the class i'm not sure how i would convert a class to lua does anyone know how i would go about converting a class from python to lua?
python class
class
The OOP magic can be done with Lua tables and metatables.
Python
Look at The __init__() Function example here...
https://www.w3schools.com/python/python_classes.asp
Lets implement this with Lua...
( Lua 5.4 interactive console )
> _VERSION
Lua 5.4
> Person = setmetatable({},{
__call = function(self, name, age)
self.name = name
self.age = age
return {name = self.name, age = self.age}
end
})
> p1 = Person("John", 36)
> print(p1.name, p1.age)
John 36
So, some readers would ask: Why a metatabled table?
A simple function could do the same job.
Once a table has a metatable it is quit simple to add methods...
> Person = setmetatable({},{
__call = function(self, name, age)
self.name, self.age = name, age
table.insert(self, {name = self.name, age = self.age}) -- Numbered keys are the Data part
return {name = self.name, age = self.age}
end,
__index = {list = function(self) for i = 1, #self do print(self[i].name, self[i].age) end end}
})
> Person("John", 31);
> Person("Jack", 32);
> Person("Jim", 33);
> Person:list()
John 31
Jack 32
Jim 33
...that can handle the data of the table by itself.
Its a great benefit that the behaviour of a table can be controlled with metatables/methods.
And above example is only the tip of the iceberg.
Have a look at the __index of a string...
> for key, value in pairs(getmetatable(_VERSION).__index) do print(key, "=", value) end
byte = function: 0x565d6f20
lower = function: 0x565d4d90
len = function: 0x565d4750
sub = function: 0x565d7210
dump = function: 0x565d5d00
gsub = function: 0x565d7dc0
char = function: 0x565d5060
unpack = function: 0x565d6530
match = function: 0x565d7da0
packsize = function: 0x565d6420
pack = function: 0x565d6950
upper = function: 0x565d4ac0
format = function: 0x565d52d0
reverse = function: 0x565d4b50
find = function: 0x565d7db0
gmatch = function: 0x565d70d0
rep = function: 0x565d4be0
Therefore this is possible...
> print(("koyaanisqatsi"):upper():reverse())
ISTAQSINAAYOK
Means: Methods can be chained if their returning datatype has a method
Exceptions are: len() accept but dont return a string and sub() needs numbers before it returns a string also dump() needs a self defined function and returns a binary (almost unreadable) string.
Can anyone fix my buggy Lua object system?
Different instances have different numeric fields
But .. when I add tables to my initialization fields, then those tables are shared between different instances (see example, below).
What I think is that I need to do a deep copy on initial fields but I can't see where. My code is below. Any suggestions?
Object = {}
function Object:new (o)
-- o = deep_copy(o) or {} -- <== this didn't work
-- self = deep copy(self) -- <== this didn't work
o = o or {}
setmetatable(o, self)
self.__index = self
self.__tostring = show
return o
end
Account = Object:new{balance = 0,all={}}
function Account:push(v)
self.all[#self.all+1] = v
end
function Account:deposit(v)
self.balance = self.balance + v end
function Account:withdraw (v)
if v > self.balance then
error"insufficient funds" end
self.balance = self.balance - v
end
function show(i, str,sep)
str,sep = "{",""
for k,v in pairs(i) do
if type(v) ~= "function" then
str = str..sep..tostring(k)..":"..tostring(v)
sep = ", "
end
end
return str .. "}"
end
Just to illustrate the problem, below I have two instances a and b
When I update a,b's numeric fields, then different instances get different values.
But when I update the all table in one instance a, it changes it in the other instance b.
a=Account:new()
b=Account:new()
a:deposit(100)
b:deposit(200)
b:push(10)
b:push(20)
a:push(300)
print("a all", show(a), show(a.all))
print("b all", show(b), show(b.all))
The output should be:
a all {balance:100} {3:300}
b all {balance:200} {1:10, 2:20}
But what actually comes out is:
a all {balance:100} {1:10, 2:20, 3:300}
b all {balance:200} {1:10, 2:20, 3:300}
You do self.__index = self, and your prototype has an all field, but you never set one in the objects that you create. As a result, accesses to it (as push does) will always go through __index and end up hitting the prototype.
As Joseph said, you need to create distinct all for every object.
You could either write function Account:new() or write Object:new() function which accepts initializer (so that you would not need to implement distinct :new() for every class):
Object = {__init = function(o) end}
function Object:new(o, init)
-- when creating an instance:
-- o: the object itself
-- when creating a class:
-- o: table containing shared fields and methods
-- init: initializer for instances of this class
o = o or {}
self.__init(o)
setmetatable(o, self)
self.__index = self
if init then
function o.__init(o)
self.__init(o)
init(o)
end
end
return o
end
Account = Object:new({}, function(o) o.all={}; o.balance=0; end)
function Account:push(v)
self.all[#self.all+1] = v
end
function Account:deposit(v)
self.balance = self.balance + v
end
function Account:withdraw (v)
if v > self.balance then
error"insufficient funds" end
self.balance = self.balance - v
end
function show(i, str,sep)
str,sep = "{",""
for k,v in pairs(i) do
if type(v) ~= "function" then
str = str..sep..tostring(k)..":"..tostring(v)
sep = ", "
end
end
return str .. "}"
end
Object.__tostring = show
a=Account:new()
b=Account:new()
a:deposit(100)
b:deposit(200)
b:push(10)
b:push(20)
a:push(300)
print("a all", show(a), show(a.all))
print("b all", show(b), show(b.all))
If you'd like to inherit from Object, easiest is to create a new constructor for account as well. This should set a balance and and all for each object, not on the class itself.
Object = {}
Account = {}
function Object:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
self.__tostring = show
return o
end
function Account:new(o)
o = o or Object:new(o)
setmetatable(o, self)
self.__index = self
o.balance = 0
o.all = {}
return o
end
a all {all:table: 0x55ab93ec70d0, balance:100} {1:300}
b all {all:table: 0x55ab93ec6ed0, balance:200} {1:10, 2:20}
Using the ROW-DISPLAY trigger when creating a dynamic OpenEdge browse. The problem is, when the data is displayed, all rows have the BGCOLOR determined by the data buffer's content of the LAST row only. Never had this problem with a static browse.
Browse code looks like this:
CREATE BROWSE l-browse-wh IN WIDGET-POOL "pool"
ASSIGN X = 1
Y = l-browse-y
FONT = INT(get-fn("browsedat-fnt"))
HEIGHT-PIXELS = l-h
WIDTH-PIXELS = FRAME f-data:WIDTH-PIXELS - 20
QUERY = hQuery
HIDDEN = FALSE
ROW-HEIGHT-CHARS = 0.67
READ-ONLY = FALSE
ROW-MARKERS = FALSE
SEPARATORS = TRUE
COLUMN-RESIZABLE = TRUE
COLUMN-SCROLLING = TRUE
FIT-LAST-COLUMN = FALSE
SCROLLBAR-VERTICAL = FALSE
FRAME = FRAME f-data:HANDLE
VISIBLE = TRUE
SENSITIVE = TRUE
TRIGGERS:
ON ROW-DISPLAY
PERSISTENT RUN row-color-ip IN THIS-PROCEDURE.
END TRIGGERS.
PROCEDURE row-color-ip:
DEF VAR l-bgcolor AS INT NO-UNDO.
IF tt-sold.t-exclude-sw /* <-- always uses value from last browse row */
THEN ASSIGN l-bgcolor = 8.
ELSE ASSIGN l-bgcolor = 15.
FOR EACH tt-col-handles
NO-LOCK:
ASSIGN tt-col-handles.t-wh:BGCOLOR = l-bgcolor.
END. /* of "FOR EACH tt-col-handles" */
END PROCEDURE.
It's something with your code (the part you're not showing). I used 10.2B Windows and replicated your code, filling the blanks. This little program alternates the colors, as I expected.
DEFINE VARIABLE l-browse-wh AS HANDLE NO-UNDO.
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
define temp-table tt-sold
field cod as int
field name as char
field t-exclude-sw as logical.
create query hQuery.
hQuery:set-buffers(temp-table tt-sold:default-buffer-handle).
hQuery:query-prepare('for each tt-sold').
define temp-table tt-col-handles
field i as int
field t-wh as handle.
create widget-pool 'pool'.
define frame f-data with size 75 by 20.
CREATE BROWSE l-browse-wh IN WIDGET-POOL "pool"
ASSIGN X = 1
width = 60
height = 10
QUERY = hQuery
HIDDEN = FALSE
ROW-HEIGHT-CHARS = 0.67
READ-ONLY = FALSE
ROW-MARKERS = FALSE
SEPARATORS = TRUE
COLUMN-RESIZABLE = TRUE
COLUMN-SCROLLING = TRUE
FIT-LAST-COLUMN = FALSE
SCROLLBAR-VERTICAL = FALSE
FRAME = FRAME f-data:HANDLE
VISIBLE = TRUE
SENSITIVE = TRUE
TRIGGERS:
ON ROW-DISPLAY
PERSISTENT RUN row-color-ip IN THIS-PROCEDURE.
END TRIGGERS.
create tt-sold.
assign tt-sold.cod = 1 tt-sold.name = 'ABC' tt-sold.t-exclude-sw = yes.
create tt-sold.
assign tt-sold.cod = 2 tt-sold.name = 'DEF' tt-sold.t-exclude-sw = no.
create tt-sold.
assign tt-sold.cod = 3 tt-sold.name = 'GHI' tt-sold.t-exclude-sw = yes.
create tt-sold.
assign tt-sold.cod = 4 tt-sold.name = 'JKL' tt-sold.t-exclude-sw = no.
create tt-col-handles.
assign tt-col-handles.i = 1 tt-col-handles.t-wh = l-browse-wh:add-like-column('tt-sold.cod').
create tt-col-handles.
assign tt-col-handles.i = 2 tt-col-handles.t-wh = l-browse-wh:add-like-column('tt-sold.name').
create tt-col-handles.
assign tt-col-handles.i = 3 tt-col-handles.t-wh = l-browse-wh:add-like-column('tt-sold.t-exclude-sw').
hQuery:query-open().
l-browse-wh:refresh().
wait-for close of this-procedure.
PROCEDURE row-color-ip:
DEF VAR l-bgcolor AS INT NO-UNDO.
IF tt-sold.t-exclude-sw /* <-- always uses value from last browse row */
THEN ASSIGN l-bgcolor = 8.
ELSE ASSIGN l-bgcolor = 15.
FOR EACH tt-col-handles
NO-LOCK:
ASSIGN tt-col-handles.t-wh:BGCOLOR = l-bgcolor.
END. /* of "FOR EACH tt-col-handles" */
END PROCEDURE.
I apologize for the large chunk, but I couldn't just put my finger on where you're going wrong. Maybe this example will help you in some way. Hope it helps!
I've been trying to take a module in Lua and use it to imitate an object. i.e. I made a deck of cards:
local Card = require("Card")
local function firstCard()
Card.newDeck()
Card.drawCard()
return Card.getCard()
end
local function secondCard()
Card.newDeck()
Card.drawCard()
return Card.getCard()
end
first = firstCard()
second = secondCard()
print(first)
print(second)
I set first = firstCard() and second = secondCard() but when I print the two variables second occasionally results as nil. I'm honestly lost. Here's the actual module itself.
local Card = {}
local deck
local value
local number, suit
local index = 0
local getCard
local getValue
function Card.newDeck()
deck = {}
value = {}
for x = 1, 13 do
if x == 1 then
number = "Ace"
elseif x == 11 then
number = "Jack"
elseif x == 12 then
number = "Queen"
elseif x == 13 then
number = "King"
else
number = x
end
for x1 = 1, 4 do
if x1 == 1 then
suit = "Clubs"
elseif x1 == 2 then
suit = "Diamonds"
elseif x1 == 3 then
suit = "Hearts"
else
suit = "Spades"
end
index = index + 1
deck[index] = number.." of "..suit
value[index] = x
end
end
end
function Card.drawCard()
index = math.random(52)
getCard = deck[index]
getValue = value[index]
end
function Card.getCard()
return getCard
end
function Card.getValue()
return getValue
end
function Card.getIndex()
return index
end
return Card
I have limited knowledge of Lua when it comes to Object-Oriented programming and to be honest, I typically only use it for calculations or small games to keep me occupied I'm class- I'm only 16. I'm more used to Java, even though I started using Lua well before I picked up Java. I just want to know if and how I can make this work. For the most part it works, just those occasional nil values.
The problem is that you have declared the index variable local at the top level of your module. That means that the random value of index that you have calculated in your first drawCard() is reused in your second call to newDeck(). You can add print(index) at the start of newDeck() to see what I mean.
There are several ways to solve the problem. One would be to add index = 0 at the top of newDeck(). A better one would be to declare your variables with smaller scoping, i.e. make index local to each function that uses it.
Try to use this instead, seems to work fine, it should print "ERROR NO VALUE" if there is some issue with the index, if that occurs (it shouldn't though), just print the index (in the generateCard() function).
This is the test
local Card = require("Card");
local function firstCard()
Card.newDeck()
return Card.generateCard(); -- two parameters, deck and value
end
local function secondCard()
Card.newDeck()
return Card.generateCard();
end
first = firstCard()
second = secondCard()
print(first)
print(second)
This is the module
local Card = {deck={};value={};};
function Card.newDeck()
local self = Card
local deck = self.deck;
local value = self.value;
local cards = {[1]="Ace",[11]="Jack",[12]="Queen",[13]="King"};
local types = {[1]="Clubs",[2]="Diamonds",[3]="Hearts",[4]="Spades"};
for x = 1, 13 do
for i = 1, 4 do
local _card,_type=(cards[x] or x),types[i];
deck[#deck+1] = _card.." of ".._type
value[#deck+1] = x
end
end
end
function Card.generateCard()
local self = Card;
if(not math.round) then
math.round = function(value) local mi,ma=math.floor(value),math.ceil(value)return(mi+0.5>value and mi or ma)end;
end
local index = math.round(math.random(1000, 52000)/1000);
return (self.deck[index] or "NO VALUE FOR CARD"),(self.value[index] or "NO DECK FOR CARD");
end
return Card
i have strings like these:
s{1,2} = 'string';
s{2,2} = 'string2';
and in workspace structure like this
U.W.string = [2 2.5 3]
I want to check (in loop) s{1,2} or s{2,2} or s{i,2} matches any structure with the same name. If so, assign values from this structure to some variable var(i). How can it be done?
Use isfields to check, if a string is the name of a field in a struct. Then use the syntax struct.(name), where name is a string to access the field. Your code might look something like:
test = struct('hello', 'world', 'count', 42, 'mean', 10);
fields = {'test', 'count';
'hello', 'text';
'more', 'less'};
values = {pi, 'dummy', -1};
for row = 1 : size(fields, 1)
for column = 1 : size(fields, 2)
if isfield(test, fields{row, column})
test.(fields{row, column}) = values{row};
end
end
end
This converts the initial struct
test =
hello: 'world'
count: 42
mean: 10
to this one
test =
hello: 'dummy'
count: 3.1416
mean: 10
A shorter implementation is achieved by removing the inner loop and giving a cell-array to isfields:
for row = 1 : size(fields, 1)
%# Note the parenthesis instead of curly braces in the next statement.
match = isfield(test, fields(row, :));
if any(match)
test.(fields{row, match}) = values{row};
end
end
Use isfield(structName,fieldName). This should do the trick:
strings{1,1} = 'foo';
strings{1,2} = 'bar';
strings{1, 3} = 'foobar';
U.W.foo = 1;
U.W.foobar = 5;
for idx = 1:length(strings)
if(isfield(U.W,strings{1,idx}))
expression = sprintf('outvar(idx) = U.W.%s',strings{1,idx});
eval(expression);
end
end