Manipulating a struct in an existing variable - struct

Complete Elixir beginner. I am sure I am missing some fundamental issues in the following. Can someone please explain, exactly why the second code not work as the first one?
defmodule Bulb do
defstruct [:state]
def turn_on(bulb = %Bulb{}) do
%{bulb | state: :on}
end
def turn_off(bulb = %Bulb{}) do
%{bulb | state: :off}
end
end
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
bulb |> Bulb.turn_off() # --> %Bulb{state: :off}
bulb |> Bulb.turn_on() # --> %Bulb{state: :on}
versus
defmodule Bulb do
defstruct [:state]
def switch_bulb(bulb = %Bulb{}) do
case bulb do
%{state: :off} -> %{bulb | state: :on}
%{state: :on} -> %{bulb | state: :off}
end
end
end
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off} why?
(Yes, the second one also works if I do the following.)
defmodule Bulb do
defstruct [:state]
def switch_bulb(bulb = %Bulb{}) do
case bulb do
%{state: :off} -> %{bulb | state: :on}
%{state: :on} -> %{bulb | state: :off}
end
end
end
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
bulb = bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off}
bulb = bulb |> Bulb.switch_bulb() # --> %Bulb{state: :on}

In elixir, everything is immutable. bulb |> Bulb.turn_on() from the first snippet does not do what you think it does. It does literally nothing, actually, “switching” the state from on (initial value) to on.
To understand, what happens, do print bulb every time.
bulb = %Bulb{state: :on} # %Bulb{state: :on}
bulb |> IO.inspect() |> Bulb.turn_off() |> IO.inspect() # on → off
bulb |> IO.inspect() |> Bulb.turn_on() |> IO.inspect() # on → on
bulb from inside switch_bulb/1 function has the same name, but a very different scope, it’s not the same variable. bulb stays what it was assigned to.
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
IO.inspect(bulb) # --> %Bulb{state: :on}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off}
bulb # --> %Bulb{state: :on}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off} why?
Even more: the example below won’t change the value of outermost bulb too
bulb = :on
if true, do: bulb = :off # different scope, outermost `bulb` is untouched
IO.inspect(bulb) #⇒ :on
That said unless you reassign the value of any expression, it simply gets lost. Nothing is mutated in place.
BTW, = is NOT an assignment, it’s a matching operator. In this case, it’s rebinding the variable.

Related

savefig and main thread is not in main loop in python3

After using
plt.savefig() in a function, I obtain
File "/usr/lib/python3.8/tkinter/__init__.py", line 4017, in __del__
self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Tcl_AsyncDelete: async handler deleted by the wrong thread
Aborted (core dumped)
I tried to add
plt.switch_backend('agg')
however, the code is not going to finish after this edit.
What to do, please?
I have downloaded https://github.com/amerand/PMOIRED
and I added
plt.savefig('bootstrap1.png')
before return in the function showBootstrap in the file oimodels.py.
def showBootstrap(b, indx=None, fig=0, figWidth=None, showRejected=False,
combParam={}, sigmaClipping=4.5, showChi2=False):
"""
you can look at combination of parameters:
combParam: {'sep':'np.sqrt($x**2 + $y**2)'} assuming 'x' and 'y' are parameters from the model
"""
global _AX, _AY
boot = copy.deepcopy(b)
# -- THIS IS NOT WORKING :(
#if showChi2:
# if not type(combParam) == dict:
# combParam = {}
# combParam.update({'_chi2': 'chi2'})
#boot['fitOnly'] = boot['fitOnly']
if len(combParam)>0:
s = '$'
for k in combParam:
if not k in boot['fitOnly']:
boot['fitOnly'].append(k)
for i,f in enumerate(boot['fitOnly']):
if 'chi2' in k:
boot['all fits'][i]['best'][k] = boot['all fits'][i]['chi2']
boot['all fits'][i]['uncer'][k] = 0.0
else:
tmp = combParam[k]+''
j = 0
while s in tmp and j<5:
for x in boot['best'].keys():
if s+x in tmp:
tmp = tmp.replace(s+x, '('+str(boot['best'][x])+')')
j+=1
boot['all fits'][i]['best'][k] = eval(tmp)
boot['all fits'][i]['uncer'][k] = 0.0
print('analyse')
boot = analyseBootstrap(boot, indx=inxd, inx=None, verbose=2, sigmaClipping=sigmaClipping)
print('done')
if figWidth is None:
figWidth = min(9.5, 1+2*len(boot['fitOnly']))
fontsize = max(min(4*figWidth/len(boot['fitOnly']), 14), 6)
plt.close(fig)
plt.figure(fig, figsize=(figWidth, figWidth))
_AX = {}
color1 = 'orange'
color2 = (0.2, 0.4, 1.0)
color3 = (0.8, 0.2, 0.4)
colorC2 = (0, 0.4, 0.2)
combi = False
# -- for each fitted parameters, show histogram
showP = sorted(boot['fitOnly'])
if showChi2:
showP.append('chi2')
offs = {}
amps = {}
for i1, k1 in enumerate(showP):
# -- create histogram plot
_AX[i1] = plt.subplot(len(showP),
len(showP),
1+i1*len(showP)+i1)
if k1=='chi2':
# -- assumes bins is already defined (chi2 cannot be first!)
# -- show histogram: line and area
h = plt.hist(boot['all chi2'], bins=bins,
color=colorC2, histtype='step', alpha=0.9)
h = plt.hist(boot['all chi2'], bins=bins,
color=colorC2, histtype='stepfilled', alpha=0.05)
plt.plot(boot['fit to all data']['chi2'],
0.4*max(h[0]), 's', markersize=fontsize/2,
color=colorC2, label='fit to all data')
plt.title(r'$\chi_{\rm red}^2$', fontsize=fontsize)
offs['chi2'] = 0
amps['chi2'] = 1
plt.xlim(min(min(h[1]),
boot['fit to all data']['chi2']-0.1*np.ptp(h[1])),
max(max(h[1]),
boot['fit to all data']['chi2']+0.1*np.ptp(h[1])))
#print('Xlim:', min(h[1]), max(h[1]), boot['fit to all data']['chi2'])
else:
# -- guess number of bins
if k1 in boot['uncer']:
bins = int(3*np.ptp(boot['all best'][k1])/boot['uncer'][k1])
else:
bins = 10
bins = min(bins, len(boot['mask'])//5)
bins = max(bins, 5)
nd = np.abs(np.mean(boot['all best'][k1])/np.ptp(boot['all best'][k1]))
if nd>0:
nd = int(np.log10(nd))
if nd>=4:
offs[k1] = np.round(np.mean(boot['all best'][k1]), nd)
amps[k1] = 10**(nd+1)
else:
offs[k1] = 0.0
amps[k1] = 1.0
#print(k1, nd, offs[k1])
# -- show histogram: line and area
h = plt.hist(amps[k1]*(boot['all best'][k1]-offs[k1]), bins=bins,
color='k', histtype='step', alpha=0.9)
h = plt.hist(amps[k1]*(boot['all best'][k1]-offs[k1]), bins=bins,
color='k', histtype='stepfilled', alpha=0.05)
# -- fitted and bootstrap values and uncertainties
if k1 in boot['fit to all data']['best']:
plt.errorbar(amps[k1]*(boot['fit to all data']['best'][k1]-offs[k1]),
0.4*max(h[0]), markersize=fontsize/2,
xerr=amps[k1]*boot['fit to all data']['uncer'][k1],
color=color1, fmt='s', capsize=fontsize/2,
label='fit to all data')
combi = False
else:
combi = True
plt.errorbar(amps[k1]*(boot['best'][k1]-offs[k1]), 0.5*max(h[0]),
xerr=amps[k1]*boot['uncer'][k1],
color=color3 if combi else color2, fmt='d',
capsize=fontsize/2, label='bootstrap', markersize=fontsize/2)
n = int(np.ceil(-np.log10(boot['uncer'][k1])+1))
fmt = '%s=\n'+'%.'+'%d'%n+'f'+'$\pm$'+'%.'+'%d'%n+'f'
plt.title(fmt%(k1, boot['best'][k1], boot['uncer'][k1]),
fontsize=fontsize)
plt.legend(fontsize=5)
# -- title
_AX[i1].yaxis.set_visible(False)
if i1!=(len(showP)-1):
_AX[i1].xaxis.set_visible(False)
else:
_AX[i1].tick_params(axis='x', labelsize=fontsize*0.8)
_AX[i1].set_xlabel(k1, fontsize=fontsize)
if offs[k1]<0:
_AX[i1].set_xlabel(k1+'\n+%f (%.0e)'%(np.abs(offs[k1]), 1/amps[k1]),
fontsize=fontsize)
elif offs[k1]>0:
_AX[i1].set_xlabel(k1+'\n-%f (%.0e)'%(np.abs(offs[k1]), 1/amps[k1]),
fontsize=fontsize)
_AX[i1].callbacks.connect('ylim_changed', _callbackAxes)
# -- end histogram
_AY = {}
# -- show density plots
for i1, k1 in enumerate(showP):
for i2 in range(i1+1, len(showP)):
k2 = showP[i2]
if i1==0:
_AY[i2] = plt.subplot(len(showP),
len(showP),
1+i2*len(showP)+i1,
sharex=_AX[i1])
ax = _AY[i2]
else:
ax = plt.subplot(len(showP),
len(showP),
1+i2*len(showP)+i1,
#sharex=_AX[i1],
sharey=_AY[i2])
#if k1=='chi2' or k2=='chi2':
# continue
#print(k1, k2)
c, m = 'k', '.'
if k1=='chi2':
X1 = boot['all chi2']
X1r = boot['all chi2 ignored']
c = colorC2
else:
X1 = boot['all best'][k1]
X1r = boot['all best ignored'][k1]
if k2=='chi2':
X2 = boot['all chi2']
X2r = boot['all chi2 ignored']
c = colorC2
else:
X2 = boot['all best'][k2]
X2r = boot['all best ignored'][k2]
plt.plot(amps[k1]*(X1-offs[k1]), amps[k2]*(X2-offs[k2]), m,
alpha=np.sqrt(2/len(boot['mask'])), color=c)
if showRejected:
plt.plot(amps[k1]*(X1r-offs[k1]), amps[k2]*(X2r-offs[k2]),
'xr', alpha=0.3)
# -- combined parameters function of the other one?
#print(combParam, k1, k2)
if (k1 in combParam and k2 in combParam[k1]) or \
(k2 in combParam and k1 in combParam[k2]):
_c = color3
else:
_c = color2
#x, y = dpfit.errorEllipse(boot, k1, k2)
if k1=='chi2':
_i1 = len(boot['fitOnly'])
_c = colorC2
else:
_i1 = boot['fitOnly'].index(k1)
if k2=='chi2':
_i2 = len(boot['fitOnly'])
_c = colorC2
else:
_i2 = boot['fitOnly'].index(k2)
t = np.linspace(0,2*np.pi,100)
sMa, sma, a = dpfit._ellParam(boot['cov'][_i1,_i1],
boot['cov'][_i2,_i2],
boot['cov'][_i1,_i2])
_X,_Y = sMa*np.cos(t), sma*np.sin(t)
_X,_Y = _X*np.cos(a)+_Y*np.sin(a),-_X*np.sin(a)+_Y*np.cos(a)
if (k1.endswith(',x') and k2.endswith(',y')) or \
(k1.endswith(',y') and k2.endswith(',x')):
print('ellipse (emin, emax, PA) for %s/%s: %.4f %.4f %.1f'%(
k1, k2, sMa, sma, a*180/np.pi))
if k1=='chi2':
x = np.mean(boot['all chi2'])+_X
else:
x = boot['best'][k1]+_X
if k2=='chi2':
y = np.mean(boot['all chi2'])+_Y
else:
y = boot['best'][k2]+_Y
plt.plot(amps[k1]*(x-offs[k1]),
amps[k2]*(y-offs[k2]), '-', color=_c,
label='c=%.2f'%boot['cord'][k1][k2])
plt.legend(fontsize=5)
if k1 in boot['fit to all data']['best'] and \
k2 in boot['fit to all data']['best']:
plt.plot(amps[k1]*(boot['fit to all data']['best'][k1]-offs[k1]),
amps[k2]*(boot['fit to all data']['best'][k2]-offs[k2]),
'x', color='0.5')
x, y = dpfit.errorEllipse(boot['fit to all data'], k1, k2)
plt.plot(amps[k1]*(x-offs[k1]), amps[k2]*(y-offs[k2]), '-',
color=color1)#, label='c=%.2f'%boot['cord'][k1][k2])
plt.plot(amps[k1]*(boot['best'][k1]-offs[k1]),
amps[k2]*(boot['best'][k2]-offs[k2]),
'+', color=_c)
if i2==(len(showP)-1):
plt.xlabel(k1, fontsize=fontsize)
if offs[k1]<0:
plt.xlabel(k1+'\n+%f (%.0e)'%(np.abs(offs[k1]), 1/amps[k1]),
fontsize=fontsize)
elif offs[k1]>0:
plt.xlabel(k1+'\n-%f (%.0e)'%(np.abs(offs[k1]), 1/amps[k1]),
fontsize=fontsize)
ax.tick_params(axis='x', labelsize=fontsize*0.8)
else:
ax.xaxis.set_visible(False)
if i1==0:
plt.ylabel(k2, fontsize=fontsize)
if offs[k2]<0:
plt.ylabel(k2+'\n+%f (%.0e)'%(np.abs(offs[k2]), 1/amps[k2]),
fontsize=fontsize)
elif offs[k2]>0:
plt.ylabel(k2+'\n-%f (%.0e)'%(np.abs(offs[k2]), 1/amps[k2]),
fontsize=fontsize)
_AY[i2].callbacks.connect('ylim_changed', _callbackAxes)
_AY[i2].tick_params(axis='y', labelsize=fontsize*0.8)
else:
ax.yaxis.set_visible(False)
plt.tight_layout()
plt.subplots_adjust(hspace = 0, # 0.65*fig.subplotpars.hspace,
wspace = 0, # 0.65*fig.subplotpars.wspace
)
plt.savefig('bootstrap1.png')
return

Concatenate Multiple QSplitter in PyQt5

I have 3 QWidgets which I want to concatenate. Basically it should look like this:
| |
| |
QWidget 1 | QWidget 2 | QWidget 3
| |
| |
QSplitter1 QSplitter2
What I tried so far is:
1) Adding each widget to the corresponding QSplitter
widget1 = QWidget()
widget2 = QWidget()
widget3 = QWidget()
splitter1 = QSplitter(self)
splitter2 = QSplitter(self)
splitter1.addWidget(widget1)
splitter1.addWidget(widget2)
splitter2.addWidget(widget2)
splitter2.addWidget(widget3)
Here I get one moveable splitter, which should be splitter2
2) Adding the complete splitter1 Widget to splitter2 as first argument
widget1 = QWidget()
widget2 = QWidget()
widget3 = QWidget()
splitter1 = QSplitter(self)
splitter2 = QSplitter(self)
splitter1.addWidget(widget1)
splitter1.addWidget(widget2)
splitter2.addWidget(splitter1)
splitter2.addWidget(widget3)
Here only the first splitter is active.
What is the correct approach doing this?
QSplitter is a container widget, and it behaves similarly to a QBoxLayout, adding the possibility of resizing the items. This means that you (normally) only need one splitter, no matter how many widgets you are adding in the same orientation.
What you are referring to as "splitters", are actually the splitter handles (QSplitterHandle classes), so the structure is actually this:
QSplitter -------------------------------------+
| | | |
h H H |
a A A |
n N N |
d QWidget 1 D QWidget 2 D QWidget 3 |
l L L |
e E E |
| | | |
+---------------+---------------+--------------+
So, that's just as easy as this:
widget1 = QWidget()
widget2 = QWidget()
widget3 = QWidget()
splitter = QSplitter(self)
splitter.addWidget(widget1)
splitter.addWidget(widget2)
splitter.addWidget(widget3)

Generating Coordinates of a N-sized diamond for Haskell

So, I have to generate the coordinates to create a diamond on a grid.
I will receive the center (x, y) and the height n from center to top, then I need to generate the coordinates [(x, y),..] to print something like this (n = 4 and center = (4, 4)):
#
# # #
# # # # #
# # # # # # #
# # # # # # # # #
# # # # # # #
# # # # #
# # #
#
Where each # represents a position of the returned list. I don't have any clue about how to do this.
This is how I do it in kotlin:
fun diamond(n: Int, coord: Coord): List<Coord> {
val coords = mutableSetOf<Coord>()
for (i in 0..n) {
for (j in 0..(n - i)) {
coords.add(Coord((coord.x + i).absoluteValue, (coord.y + j).absoluteValue))
coords.add(Coord((coord.x + i).absoluteValue, (coord.y - j).absoluteValue))
coords.add(Coord((coord.x - i).absoluteValue, (coord.y + j).absoluteValue))
coords.add(Coord((coord.x - i).absoluteValue, (coord.y - j).absoluteValue))
}
}
return coords.toList()
}
P.S.: Printing is not a problem, I just need the coordinates.
We can make an analysis of the items in the coordinate. If the center is at (x,y), then we know that there is one symbol exactly n places above the center, and 3 symbols exactly n-1 above the center. etc.
We thus can use list comprehension here:
coordinates :: Integral i => i -> i -> i -> [(i,i)]
coordinates x y n = [(x+dx, y+dy) | dy <- [-n .. n], let ay = n - abs dy, dx <- [-ay .. ay]]

Maze solver won't backtrack in Python

There are a couple of maze questions similar to this one but none of them ever really go into the why it won't work.
I don't need precise answers. I just need to know why this particular thing doesn't work.
This is the bit of my class Maze that I need help with.
ysize in my example is 10
xsize is 10
xend is 20 (changing it to 19 messes with the results and doesn't draw anything)
yend is 10 (changing it to 9 does this too)
class Maze:
def __init__(self):
self.maze = []
self.xstart = None
self.ystart = None
self.xend = None
self.yend = None
self.xsize = None
self.ysize = None
def read_maze(self, filename):
maze_list = []
f_maze = open(filename)
size = f_maze.readline().split() #
start = f_maze.readline().split() #
end = f_maze.readline().split() #
self.xstart = int(start[1])
self.ystart = int(start[0])
self.xend = (int(end[1])*2)
self.yend = (int(end[0])*2)
self.xsize = (int(size[1])*2)
self.ysize = (int(size[0])*2)
lines = f_maze.readlines()
for line in lines:
maze_list.append(list(line[:len(line)]))
self.maze = maze_list # Assigns to class
def __str__(self):
return ("".join(''.join(line) for line in self.maze))
def solve(self, x, y):
if y > (self.ysize) or x > (self.xsize):
print("1")
return False
if self.maze[y][x] == self.maze[self.yend][self.xend]:
print("2")
return True
if self.maze[y][x] != " ":
print("3")
return False
self.maze[y][x] = "o" # MARKING WITH o for path already taken.
if self.solve(x+1,y) == True:
return True
elif self.solve(x,y+1) == True:
return True
elif self.solve(x-1,y) == True:
return True
elif self.solve(x,y-1) == True:
return True
self.maze[y][x] = " " # ELSE I want it to be replaced with space
return False
This is the current result.
---------------------
|ooooooooooooo| | |
|-+-+-+ +-+-+o+ + +-|
| | | |o| |
| +-+-+ + +-+-+-+ + |
| | | | | |
|-+-+ + + + +-+ +-+-|
| | |
|-+ +-+-+-+-+-+ +-+ |
| | | |
---------------------
I want it like this:
---------------------
|ooooooo | | |
|-+-+-+o+-+-+ + + +-|
| | o| | | |
| +-+-+o+ +-+-+-+ + |
| o| | | | |
|-+-+ +o+ + +-+ +-+-|
| |ooooooooooooo|
|-+ +-+-+-+-+-+ +-+o|
| | | o|
---------------------
I don't know how to fix the indentation format here I apologize. That is my whole code. These are my test statements:
maze = Maze()
maze.read_maze(filename)
maze.solve(maze.xstart, maze.ystart)
print(maze)
The files go in this format saved as .txt files.
5 10
1 1
5 10
---------------------
| | | |
|-+-+-+ +-+-+ + + +-|
| | | | | |
| +-+-+ + +-+-+-+ + |
| | | | | |
|-+-+ + + + +-+ +-+-|
| | |
|-+ +-+-+-+-+-+ +-+ |
| | | |
---------------------
The problem is that as your file stands, xend, yend is (10, 20). To debug why it's not working, you can print(self.maze[self.yend][self.xend]) which returns a dash "-". Now, when your recursive call's (x, y) pair reaches its first dash, it tests True for the line
if self.maze[y][x] == self.maze[self.yend][self.xend]:
and thinks it has solved the maze. Rather, we want to test
if (y, x) == (self.yend, self.xend):
That is, test the coordinates, not the value of the square.
Another point: examining the actual location of the goal, we see that it's here:
+-+ +-+ |
| |
--------- <= this corner is the goal!
Which is unreachable if moving in strictly cardinal directions. Moving the goal a square up or to the left would put it within bounds of the solver algorithm.
This was sufficient to get the code working for me and hopefully is enough to get you moving again.

Clustering of a list of strings of letters according to 2 (and ideally generalized to n) arbitrary grouping rules?

I want to sort a set of strings (of letters) of variable length in n groups, based on inclusion of any/all/none letters of n given sets.
For instance, here I am trying to sort all combinations of the letters 'A,B,P,Q,X' in 2 groups, with the following rules: group1 must include all/any of 'A,P' (but not 'B,Q'), group2 must include all/any of 'B,Q' (but not 'A,P'). My final goal is to build a list that has the groups as segregated as possible (e.g. beginning and end), with strings containing no members of any group in the middle, followed by members of both groups and hybrids between the middle and the extremes. Ideally the order would be: all-1/none-2,some-1/none-2,all-1/some-2,none-1-2/some-1-2,all-2/some-1,some-2/none-1,all-2/none-1.
labels_powerset = ['A','B','P','Q','X',
'AB','AP','AQ','AX','BP','BQ','BX','PQ','PX','QX',
'ABP','ABQ','ABX','APQ','APX','AQX','BPQ','BPX','BQX','PQX',
'ABPQ','ABPX','ABQX','APQX','BPQX','ABPQX']
labels_for_order = []
for length in range(1,len(max(labels_powerset,key=len))+1):
order = [label for label in labels_powerset if len(label)==length]
labels_for_order.append(order)
group1 = ['A','P']
group2 = ['B','Q']
all1 = [y for y in[[label for label in order if all(x in label for x in group1) and not any(y in label for y in group2)]
for order in labels_for_order]if y]
any1 = [y for y in[[label for label in order if any(x in label for x in group1) and not all(x in label for x in group1) and not any(y in label for y in group2)]
for order in labels_for_order]if y]
all2 = [y for y in[[label for label in order if all(x in label for x in group2) and not any(y in label for y in group1)]
for order in labels_for_order]if y]
any2 = [y for y in[[label for label in order if any(x in label for x in group2) and not all(x in label for x in group2) and not any(y in label for y in group1)]
for order in labels_for_order]if y]
none = [y for y in[[label for label in order if not any(x in label for x in group1) and not any(y in label for y in group2)]
for order in labels_for_order]if y]
both = [y for y in[[label for label in order if any(x in label for x in group1) and any(y in label for y in group2)]
for order in labels_for_order]if y]
both1 = [both[x] for x in range(0,int(len(both)/2))]
both2 = [both[x] for x in range(int(len(both)/2),len(both))]
sorted_labels = flatten(any1+all1+both1+none+both2+all2+any2)
The objective is to have a list as symmetric as possible in terms of membership and length of the strings.
I am pretty new at coding and have read something on k-means but can't figure out how to apply it to strings of letters.
How do I do it more efficiently, and in a way that is generalizable to n groups/rules?
K-means is for multivariate continuous data, and clustering does not attempt to make balanced groups.
What you should consider is to use sorting.
Define a score function. For example, give +1 for each "good" letter, -1 for each "bad" letter, and a bonus of +-100 if it is pure.
Then sort the words based on this score.

Resources