Unpacking in a for loop twice - python

I am trying to find the last iteration in a loop so that I can skip it. This is not ideal I'm aware, however my issue is separate.
I am new to python, but if I'm not mistaken, this for loop is in unpacking the variable letter. This makes the second run through of the for loop empty or in some sense broken. If my understanding here is in anyway incorrect, feel free to comment or edit.
this_iteration = 0
for [x, y, dx, dy, r], letter in letters_positions:
last_iteration = this_iteration
this_iteration += 1
this_iteration = 0
for [x, y, dx, dy, r], letter in letters_positions:
if this_iteration == last_iteration:
continue
this_iteration += 1
I tried unsuccessfully passing this in the second for loop, but the second for loop still does not run.
for letter in letters_positions:
Is there a way for me to repack the variables together for a second run through the loop?
UPDATE: This is CairoSVG, not my own code, but I'll try to post more context the best I can. letters_positions is taken from an svg file. The important two lines that precede my code are the following.
from .helpers import distance, normalize, point_angle, zip_letters
letters_positions = zip_letters(x, y, dx, dy, rotate, node.text)
Original CairoSVG code can be found on github here.
https://github.com/Kozea/CairoSVG/blob/master/cairosvg/text.py

Edit (example):
this_iteration = 0
letters_positions = list(letters_positions)
for [x, y, dx, dy, r], letter in letters_positions:
last_iteration = this_iteration
this_iteration += 1
this_iteration = 0
for [x, y, dx, dy, r], letter in letters_positions:
if this_iteration == last_iteration:
continue
this_iteration += 1
From helpers.py in the github link you posted:
# Incidentally, they say that this method returns a list with the current letter's positions.
# This isn't true - it is returning a generator.
# To return a list, the outermost parenthesis need to be replaced with square brackets,
# or by simply adding list before the parenthesis
# i.e. [...] or list(...)
def zip_letters(xl, yl, dxl, dyl, rl, word):
"""Returns a list with the current letter's positions (x, y and rotation).
E.g.: for letter 'L' with positions x = 10, y = 20 and rotation = 30:
>>> [[10, 20, 30], 'L']
Store the last value of each position and pop the first one in order to
avoid setting an x,y or rotation value that have already been used.
"""
# Notice the parenthesis below - this is a generator that gets exhausted after one iteration
return (
([pl.pop(0) if pl else None for pl in (xl, yl, dxl, dyl, rl)], char)
for char in word)
Thus, you empty it after the first iteration. Create a list or some other data structure out of it letters_positions = list(letters_positions), and then you can loop over it as many times as you'd like.

letters_positions is a sequence. If you don't want to iterate over the final element, do iterate over letters_positions[:-1]
EDIT: if you're using Python3, you might have to call list on letters_positions first

Related

How to compare two lists of same length (but different data types) and their corresponding values?

I have two lists...
depth = [1, 2, 3, 2, 1]
direction = ['x', 'y', 'z', 'x', 'x']
I want to compare the two of these lists and for each instance of direction == 'x', continually add the corresponding values in depth together. So I should get a value of 4 since 1 + 2 + 1 = 4 and it corresponds to each 'x' string in list direction.
The lists depth and direction are greatly abbreviated for example purposes. I want to iterate over both lists and in reality they contain a couple thousand values each.
I have already created a zip() and tried working with it like this:
dndzip = zip(depth, direction)
x = 0
for i,j in dndzip:
if i == 'x':
x += j
return x
print(x)
I actually had this for loop under a defined function, but unfortunately this for loop doesn't return the proper value. I get a value and no errors, but it's always a value of 2 and not sure where I'm going wrong here.
I'm not sure why you're getting 2 exactly, as when I ran the above code I got 0. But the reason for that is because in your loop, when you're decompressing your list with
dndzip = zip(depth, direction)
for i,j in dndzip:
notice that you zipped your list with depth as the first argument, so in this case i is depth and j is direction, so x+=j is never run because i (depth) is never 'x'. To fix this, you would do:
dndzip = zip(depth, direction)
x = 0
for j,i in dndzip:
if i == 'x':
x += j
print(x)
In your particular code, it looks like you have the i and j mixed up. When you do dndzip = zip(depth, direction), dndzip is going to contain tuples where the first item is the item from depth, and the second is the one from direction. Meaning that something like
def sum_x_depths(depth, direction):
dndzip = zip(depth, direction)
x = 0
for i,j in dndzip:
if i == 'x':
x += j
return x
will fail, since things like if i == 'x': are comparing i, which is an int, to 'x' which is a str. Then x += i also fails, because j is a str and x is an int. Make sure to try and make variable names refer to what they represent!
def sum_x_depths(depths, directions):
s = 0
for depth, direction in zip(depths, directions):
if direction == 'x':
s += depth
return s
You can also use the sum function and generator comprehensions to nice effect here, though it might be a bit long on a single line with these names:
def sum_x_depths(depths, directions):
return sum(depth for depth, direction in zip(depths, directions) if direction == 'x')

Robot return to origin

Question:
There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its moves, judge if this robot ends up at (0, 0) after it completes its moves.
The move sequence is represented by a string, and the character moves[i] represents its ith move. Valid moves are R (right), L (left), U (up), and D (down). If the robot returns to the origin after it finishes all of its moves, return true. Otherwise, return false.
Note: The way that the robot is "facing" is irrelevant. "R" will always make the robot move to the right once, "L" will always make it move left, etc. Also, assume that the magnitude of the robot's movement is the same for each move.
Input: moves = "UD"
Output: true
Explanation: The robot moves up once, and then down once.
All moves have the same magnitude, so it ended up at the origin where it started.
Therefore, we return true.
I have the following solution, which seems to be wrong for sequences = "UD", which should return True. Could someone help me understand what I am doing wrong here and how I could fix it?
class Solution:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
return x + self.x, y + self.y
WALKS = dict(U=Mover(0, -1), D=Mover(0, 1),
L=Mover(-1, 0), R=Mover(1, 0))
def judge_circle(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return x == y == 0
def move_sequences(self,sequences):
for moves in sequences:
return (solution.judge_circle(moves))
if __name__ == "__main__":
solution = Solution()
sequences = "UD"
print(solution.move_sequences(sequences))
This solution seems overthinking it by quite a bit. You can just make a counter of each of the 4 directions and figure out if you have the same number of Us relative to Ds and Ls relative to Rs. return s.count("U") == s.count("D") and s.count("L") == s.count("R") gives you a linear solution that can be optimized into a single pass with something like
from collections import Counter
d = Counter(moves)
return d["D"] == d["U"] and d["R"] == d["L"]
As for your code,
for moves in sequences:
return (solution.judge_circle(moves))
looks funny to me. Returning on the first iteration means the loop is pointless. moves here is misleadingly named -- it's only a single character "U". judge_circle already does a loop, so if you really want to brute-force it, you'll only want one loop over the sequence rather than two.
Your task is simple:
def judge_circle(moves):
if moves.lower().count('U') == moves.lower().count('D') and moves.lower().count('L') == moves.lower().count('R'):
return True
else:
return False
print(judge_circle('UD'))
You only have to check whether the number of 'ups' equals the numbers of 'downs', and 'lefts' equal 'rights'.
Ok, a part from refactor advices, you can fix your script in a easy way.
def move_sequences(self,sequences):
for moves in sequences:
return (solution.judge_circle(moves))
fails because you pass a string in sequences and the for loop cycles over the letters, passing every single letter to judge_circle.
Remove the for loop and pass sequences to judge_circle!

Appending the list with each unique solution to the Queens Puzzle. I don't know why the last solution is the only solution appended to the list?

I am trying to save each individual solution to the N-Queens Puzzle to a list. However, when I try to add each list as a sub-list, only the last solution is added to the list (10 times), as opposed to the 10 individual solutions. My goal is to ensure that each time I run the board only new solutions, which have not been found already, are printed and added to the list.
def share_diagonal(x0, y0, x1, y1):
""" Is (x0, y) on the same shared diagonal with (x1, y1)? """
dx = abs(x1 - x0) # Calc the absolute y distance
dy = abs(y1 - y0) # Calc the absolute x distance
return dx == dy # They clash if dx == yx
def col_clashes(bs, c):
""" Return True if the queen at column c clashes
with any queen to its left.
"""
for i in range(c): # Look at all columns to the left of c
if share_diagonal(i, bs[i], c, bs[c]):
return True
return False # No clashes - col c has a safe placement
def has_clashes(the_board):
""" Determine whether we have any queens clashing on the diagonal.
We're assuming here that the_board is a permutation of column
numbers, so we're not explicitly checking row or column clashes.
"""
for col in range(1, len(the_board)):
if col_clashes(the_board, col):
return True
return False
solutions = []
def main(board_size):
import random
global solutions
rng = random.Random() # Instantiate a generator
bd = list(range(board_size)) # Generate the initial permutation
num_found = 0
tries = 0
while num_found < 10:
rng.shuffle(bd)
tries += 1
if not has_clashes(bd):
print("Found solution {0} in {1} tries.".format(bd, tries))
solutions.append(bd) # This is the section in which I am trying to save each individual solution into a list.
tries = 0
num_found += 1
main(8)
for i in solutions:
print(i) # When I print off the list, all items in the list are replica's of the last solution found. Not each individual solution. I don't know why this is occurring.
Just change
solutions.append(bd)
to
solutions.append(list(bd))
which creates a copy of your current solution.
Your problem with having the same solution 10 times in the list is because you add a reference to bd in your result list but shuffle the list bd in-place afterwards.
In order to skip duplicate solutions change
if not has_clashes(bd):
to
if not has_clashes(bd) and bd not in solutions:

Python nested loop to identify characters in string of text

I have a string of text that reads:
1x2xx1x2xx1x2xx1x2xxx
I need to take apart the string of text, and if it is a number, i want to pass that number along with a few other variables to another function to print a square on a canvas.
I wrote the following code:
def process_single_line(a_canvas, line_of_pattern, left, top, size):
x = left
y = top
for char in line_of_pattern:
if char.isdigit():
type_of_tile = int(char)
draw_tile (a_canvas, type_of_tile, x, y, size)
else:
x += size
The issue I am having is:
It doesn't seem to work, the rectangles and shapes that draw_tile
is supposed to print don't appear (but the draw_tile function
works fine, because it is being referenced multiple times else where
in the program where it prints just perfect)
At the end of the loop I want to increase the y value by y+=size as
well, so that when the NEXT string of text is passed through the
function it moves to the NEXT grid line.
Expected Outcome:
What I am getting vs What I am trying to get:
I believe you should always increment the x position after rendering.
try this:
def process_single_line(a_canvas, line_of_pattern, left, top, size):
x = left
y = top
for char in line_of_pattern:
if char.isdigit():
type_of_tile = int(char)
draw_tile (a_canvas, type_of_tile, x, y, size)
x += size
Multiline solution (in case you didn't have it)
def process_single_line(a_canvas, line_of_pattern, left, top, size):
x = left
y = top
for char in line_of_pattern:
if char.isdigit():
type_of_tile = int(char)
draw_tile(a_canvas, type_of_tile, x, y, size)
x += size
lines = ['1x2xx1x2xx1x2xx1x2xxx', '3xxxx3xxxx3xxxx3xxxx']
for line_num, line in enumerate(lines):
process_single_line(canvas, line, 0, size*line_num, size)

Python: inserting a 2D list inside another replacing content

I've written a small (destructive) function that takes two 2D lists (or "grids" as I call them) and a set of coordinates. It inserts the first grid inside the second grid, assumed to be as big or bigger as the first one (no checks implemented). The coordinates denote the top-left corner of the first grid.
def insert_grid(subj, obj, cords=(0, 0)):
u, v = cords
h = len(subj)
w = len(subj[0])
for y in range(0, h):
for x in range(0, w):
obj[u + y][v + x] = subj[y][x]
I was wondering if there was a cleaner, more pythonic way to achieve the same effect. The standard lib methodology would as always be prefered above everything else.
Thank you. Alisa.
You can simplify slightly:
def insert_grid(subj, obj, cords=(0, 0)):
u, v = cords
w = len(subj[0])
for index, row in enumerate(subj, u):
obj[index][v:v+w] = row
This replaces all columns in a row in one go.

Categories