问:
How to plot geodesic curves on a surface embedded in 3D?
I have in mind this video, or this simulation, and I would like to reproduce the geodesic lines on some sort of surface in 3D, given by a function f(x,y), from some starting point.

The midpoint method seems computationally and code intense, and so I'd like to ask if there is a way to generate an approximate geodesic curve based on the normal vector to the surface at different points. Each point has a tangent vector space associated with it, and therefore, it seems like knowing the normal vector does not determine a specific direction to move forward the curve.

I have tried working with Geogebra, but I realize that it may be necessary to shift to other software platforms, such as Python (or Poser?), Matlab, or others.

Is this idea possible, and can I get some ideas as to how to implement it?

In case it provides some ideas as to how to answer the question, there previously was an answer (now unfortunatley erased) suggesting the midpoint method for a terrain with the functional form z = F(x,y), starting with the straight line between the endpoints, splitting in short segments [I presume the straight line on the XY plane (?)], and lifting [I presume the nodes between segments on the XY plane (?)] on the surface. Next it suggested finding "a midpoint" [I guess a midpoint of the segments joining each consecutive pairs of projected points on the surface (?)], and projecting "it" [I guess each one of these midpoints close, but not quite on the surface(?)] orthogonally on the surface (in the direction of the normal), using the equation Z + t = F(X + t Fx, Y + t Fy) [I guess this is a dot product meant to be zero...

(?)], where (X,Y,Z) are the coordinates of the midpoint, Fx, Fy the partial derivatives of F, and t the unknown [that is my main issue understanding this... What am I supposed to do with this t once I find it? Add it to each coordinate of (X,Y,Z) as in (X+t, Y+t, Z+t)? And then?]. This is a non-linear equation in t, solved via Newton's iterations.

As an update / bookmark, Alvise Vianello has kindly posted a Python computer simulation of geodesic lines inspired on this page on GitHub. Thank you very much!

答:
I have an approach that should be applicable to an arbitrary 3D surface, even when that surface has holes in it or is noisy.  It's pretty slow right now, but it seems to work and may give you some ideas for how to do this.

The basic premise is a differential geometric one and is to:

1.) Generate a pointset representing your surface

2.) Generate a k nearest neighbors proximity graph from this pointset (I also normalized distances across dimensions here as I felt it captured the notion of "neighbors" more accurately)

3.) Calculate the tangent spaces associated with each node in this proximity graph by using the point and its neighbors as columns of a matrix that I then perform SVD on.  After SVD, the left singular vectors give me a new basis for my tangent space (the first two column vectors are my plane vectors, and the third is normal to the plane)

4.) Use dijkstra's algorithm to move from a starting node to an ending node on this proximity graph, but instead of using euclidean distance as edge weights,  use the distance between vectors being parallel transported via tangent spaces.

It's inspired by this paper (minus all the unfolding): https://arxiv.org/pdf/1806.09039.pdf

Note that I left a few helper functions I was using in that probably aren't relevant to you directly (the plane plotting stuff mostly).

The functions you'll want to look at are get_knn, build_proxy_graph, generate_tangent_spaces, and geodesic_single_path_dijkstra.

The implementation could also probably be improved.

Here's the code:

```<code>import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mayavi import mlab
from sklearn.neighbors import NearestNeighbors
from scipy.linalg import svd
import networkx as nx
import heapq
from collections import defaultdict

def surface_squares(x_min, x_max, y_min, y_max, steps):
    x = np.linspace(x_min, x_max, steps)
    y = np.linspace(y_min, y_max, steps)
    xx, yy = np.meshgrid(x, y)
    zz = xx**2 + yy**2
    return xx, yy, zz

def get_meshgrid_ax(x, y, z):
    # fig = plt.figure()
    # ax = fig.gca(projection='3d')
    # ax.plot_surface(X=x, Y=y, Z=z)
    # return ax
    fig = mlab.figure()
    su = mlab.surf(x.T, y.T, z.T, warp_scale=0.1)

def get_knn(flattened_points, num_neighbors):
    # need the +1 because each point is its own nearest neighbor
    knn = NearestNeighbors(num_neighbors+1)
    # normalize flattened points when finding neighbors
    neighbor_flattened = (flattened_points - np.min(flattened_points, axis=0)) / (np.max(flattened_points, axis=0) - np.min(flattened_points, axis=0))
    knn.fit(neighbor_flattened)
    dist, indices = knn.kneighbors(neighbor_flattened)
    return dist, indices

def rotmatrix(axis, costheta):
    """ Calculate rotation matrix

    Arguments:
    - `axis`     : Rotation axis
    - `costheta` : Rotation angle
    """
    x, y, z = axis
    c = costheta
    s = np.sqrt(1-c*c)
    C = 1-c
    return np.matrix([[x*x*C+c,    x*y*C-z*s,  x*z*C+y*s],
                      [y*x*C+z*s,  y*y*C+c,    y*z*C-x*s],
                      [z*x*C-y*s,  z*y*C+x*s,  z*z*C+c]])

def plane(Lx, Ly, Nx, Ny, n, d):
    """ Calculate points of a generic plane

    Arguments:
    - `Lx` : Plane Length first direction
    - `Ly` : Plane Length second direction
    - `Nx` : Number of points, first direction
    - `Ny` : Number of points, second direction
    - `n`  : Plane orientation, normal vector
    - `d`  : distance from the origin
    """

    x = np.linspace(-Lx/2, Lx/2, Nx)
    y = np.linspace(-Ly/2, Ly/2, Ny)
    # Create the mesh grid, of a XY plane sitting on the orgin
    X, Y = np.meshgrid(x, y)
    Z = np.zeros([Nx, Ny])
    n0 = np.array([0, 0, 1])

    # Rotate plane to the given normal vector
    if any(n0 != n):
        costheta = np.dot(n0, n)/(np.linalg.norm(n0)*np.linalg.norm(n))
        axis = np.cross(n0, n)/np.linalg.norm(np.cross(n0, n))
        rotMatrix = rotmatrix(axis, costheta)
        XYZ = np.vstack([X.flatten(), Y.flatten(), Z.flatten()])
        X, Y, Z = np.array(rotMatrix*XYZ).reshape(3, Nx, Ny)

    eps = 0.000000001
    dVec = d #abs((n/np.linalg.norm(n)))*d#np.array([abs(n[i])/np.linalg.norm(n)*val if abs(n[i]) > eps else val for i, val in enumerate(d)]) #
    X, Y, Z = X+dVec[0], Y+dVec[1], Z+dVec[2]
    return X, Y, Z

def build_proxy_graph(proxy_n_dist, proxy_n_indices):
    G = nx.Graph()

    for distance_list, neighbor_list in zip(proxy_n_dist, proxy_n_indices):
        # first element is always point
        current_node = neighbor_list[0]
        neighbor_list = neighbor_list[1:]
        distance_list = distance_list[1:]
        for neighbor, dist in zip(neighbor_list, distance_list):
            G.add_edge(current_node, neighbor, weight=dist)
    return G

def get_plane_points(normal_vec, initial_point, min_range=-10, max_range=10, steps=1000):
    steps_for_plane = np.linspace(min_range, max_range, steps)
    xx, yy = np.meshgrid(steps_for_plane, steps_for_plane)
    d = -initial_point.dot(normal_vec)
    eps = 0.000000001
    if abs(normal_vec[2]) < eps and abs(normal_vec[1]) > eps:
        zz = (-xx*normal_vec[2] - yy*normal_vec[0] - d)/normal_vec[1]
    else:
        zz = (-xx*normal_vec[0] - yy*normal_vec[1] - d)/normal_vec[2]
    return xx, yy, zz

# def plot_tangent_plane_at_point(pointset, flattened_points, node, normal_vec):
#     ax = get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
#     node_loc = flattened_points[node]
#     print("Node loc: {}".format(node_loc))
#     xx, yy, zz = plane(10, 10, 500, 500, normal_vec, node_loc)
#     # xx, yy, zz = get_plane_points(normal_vec, node_loc)
#     print("Normal Vec: {}".format(normal_vec))
#     ax.plot_surface(X=xx, Y=yy, Z=zz)
#     ax.plot([node_loc[0]], [node_loc[1]], [node_loc[2]], markerfacecolor='k', markeredgecolor='k', marker='o', markersize=10)
#     plt.show()

def generate_tangent_spaces(proxy_graph, flattened_points):
    # This depth should gaurantee at least 16 neighbors
    tangent_spaces = {}
    for node in proxy_graph.nodes():
        neighbors = list(nx.neighbors(proxy_graph, node))
        node_point = flattened_points[node]
        zero_mean_mat = np.zeros((len(neighbors)+1, len(node_point)))
        for i, neighbor in enumerate(neighbors):
            zero_mean_mat[i] = flattened_points[neighbor]
        zero_mean_mat[-1] = node_point

        zero_mean_mat = zero_mean_mat - np.mean(zero_mean_mat, axis=0)
        u, s, v = svd(zero_mean_mat.T)
        # smat = np.zeros(u.shape[0], v.shape[0])
        # smat[:s.shape[0], :s.shape[0]] = np.diag(s)
        tangent_spaces[node] = u
    return tangent_spaces

def geodesic_single_path_dijkstra(flattened_points, proximity_graph, tangent_frames, start, end):
    # short circuit
    if start == end:
        return []
    # Create min priority queue
    minheap = []
    pred = {}
    dist = defaultdict(lambda: 1.0e+100)
    # for i, point in enumerate(flattened_points):
    R = {}
    t_dist = {}
    geo_dist = {}
    R[start] = np.eye(3)
    t_dist[start] = np.ones((3,))
    dist[start] = 0
    start_vector = flattened_points[start]
    for neighbor in nx.neighbors(proxy_graph, start):
        pred[neighbor] = start
        dist[neighbor] = np.linalg.norm(start_vector - flattened_points[neighbor])
        heapq.heappush(minheap, (dist[neighbor], neighbor))
    while minheap:
        r_dist, r_ind = heapq.heappop(minheap)
        if r_ind == end:
            break
        q_ind = pred[r_ind]
        u, s, v = svd(tangent_frames[q_ind].T*tangent_frames[r_ind])
        R[r_ind] = np.dot(R[q_ind], u * v.T)
        t_dist[r_ind] = t_dist[q_ind]+np.dot(R[q_ind], tangent_frames[q_ind].T * (r_dist - dist[q_ind]))
        geo_dist[r_ind] = np.linalg.norm(t_dist[r_ind])
        for neighbor in nx.neighbors(proxy_graph, r_ind):
            temp_dist = dist[r_ind] + np.linalg.norm(flattened_points[neighbor] - flattened_points[r_ind])
            if temp_dist < dist[neighbor]:
                dist[neighbor] = temp_dist
                pred[neighbor] = r_ind
                heapq.heappush(minheap, (dist[neighbor], neighbor))
    # found ending index, now loop through preds for path
    current_ind = end
    node_path = [end]
    while current_ind != start:
        node_path.append(pred[current_ind])
        current_ind = pred[current_ind]

    return node_path

def plot_path_on_surface(pointset, flattened_points, path):
    # ax = get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
    # ax.plot(points_in_path[:, 0], points_in_path[:, 1], points_in_path[:, 2], linewidth=10.0)
    # plt.show()
    get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
    points_in_path = flattened_points[path]
    mlab.plot3d(points_in_path[:, 0], points_in_path[:, 1], points_in_path[:, 2] *.1)
    mlab.show()

"""
    True geodesic of graph.
    Build proximity graph
    Find tangent space using geodisic neighborhood at each point in graph
    Parallel transport vectors between tangent space points
    Use this as your distance metric
    Dijkstra's Algorithm
"""
if __name__ == "__main__":
    x, y, z = surface_squares(-5, 5, -5, 5, 500)
    # plot_meshgrid(x, y, z)
    pointset = np.stack([x, y, z], axis=2)
    proxy_graph_num_neighbors = 16
    flattened_points = pointset.reshape(pointset.shape[0]*pointset.shape[1], pointset.shape[2])
    flattened_points = flattened_points
    proxy_n_dist, proxy_n_indices = get_knn(flattened_points, proxy_graph_num_neighbors)
    # Generate a proximity graph using proxy_graph_num_neighbors
    # Nodes = number of points, max # of edges = number of points * num_neighbors
    proxy_graph = build_proxy_graph(proxy_n_dist, proxy_n_indices)
    # Now, using the geodesic_num_neighbors, get geodesic neighborshood for tangent space construction
    tangent_spaces = generate_tangent_spaces(proxy_graph, flattened_points)
    node_to_use = 2968
    # 3rd vector of tangent space is normal to plane
    # plot_tangent_plane_at_point(pointset, flattened_points, node_to_use, tangent_spaces[node_to_use][:, 2])
    path = geodesic_single_path_dijkstra(flattened_points, proxy_graph, tangent_spaces, 250, 249750)
    plot_path_on_surface(pointset, flattened_points, path)
</code>```

Note that I installed and set up mayavi to get a decent output image (matplotlib doesn't have real 3d rendering and consequently, its plots suck).  I did however leave the matplotlib code in if you want to use it.  If you do, just remove the scaling by .1 in the path plotter and uncomment the plotting code. Anyways, here's an example image for z=x^2+y^2.  The white line is the geodesic path:

You could also fairly easily adjust this to return all the pairwise geodesic distances between nodes from dijkstra's algorithm (look in the appendix of the paper to see the minor modifications you'll need to do this).  Then you could draw whatever lines you want on your surface.

Body:
I have an approach that should be applicable to an arbitrary 3D surface, even when that surface has holes in it or is noisy.  It's pretty slow right now, but it seems to work and may give you some ideas for how to do this.

The basic premise is a differential geometric one and is to:

1.) Generate a pointset representing your surface

2.) Generate a k nearest neighbors proximity graph from this pointset (I also normalized distances across dimensions here as I felt it captured the notion of "neighbors" more accurately)

3.) Calculate the tangent spaces associated with each node in this proximity graph by using the point and its neighbors as columns of a matrix that I then perform SVD on.  After SVD, the left singular vectors give me a new basis for my tangent space (the first two column vectors are my plane vectors, and the third is normal to the plane)

4.) Use dijkstra's algorithm to move from a starting node to an ending node on this proximity graph, but instead of using euclidean distance as edge weights,  use the distance between vectors being parallel transported via tangent spaces.

It's inspired by this paper (minus all the unfolding): https://arxiv.org/pdf/1806.09039.pdf

Note that I left a few helper functions I was using in that probably aren't relevant to you directly (the plane plotting stuff mostly).

The functions you'll want to look at are get_knn, build_proxy_graph, generate_tangent_spaces, and geodesic_single_path_dijkstra.

The implementation could also probably be improved.

Here's the code:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mayavi import mlab
from sklearn.neighbors import NearestNeighbors
from scipy.linalg import svd
import networkx as nx
import heapq
from collections import defaultdict

def surface_squares(x_min, x_max, y_min, y_max, steps):
    x = np.linspace(x_min, x_max, steps)
    y = np.linspace(y_min, y_max, steps)
    xx, yy = np.meshgrid(x, y)
    zz = xx**2 + yy**2
    return xx, yy, zz

def get_meshgrid_ax(x, y, z):
    # fig = plt.figure()
    # ax = fig.gca(projection='3d')
    # ax.plot_surface(X=x, Y=y, Z=z)
    # return ax
    fig = mlab.figure()
    su = mlab.surf(x.T, y.T, z.T, warp_scale=0.1)

def get_knn(flattened_points, num_neighbors):
    # need the +1 because each point is its own nearest neighbor
    knn = NearestNeighbors(num_neighbors+1)
    # normalize flattened points when finding neighbors
    neighbor_flattened = (flattened_points - np.min(flattened_points, axis=0)) / (np.max(flattened_points, axis=0) - np.min(flattened_points, axis=0))
    knn.fit(neighbor_flattened)
    dist, indices = knn.kneighbors(neighbor_flattened)
    return dist, indices

def rotmatrix(axis, costheta):
    """ Calculate rotation matrix

    Arguments:
    - `axis`     : Rotation axis
    - `costheta` : Rotation angle
    """
    x, y, z = axis
    c = costheta
    s = np.sqrt(1-c*c)
    C = 1-c
    return np.matrix([[x*x*C+c,    x*y*C-z*s,  x*z*C+y*s],
                      [y*x*C+z*s,  y*y*C+c,    y*z*C-x*s],
                      [z*x*C-y*s,  z*y*C+x*s,  z*z*C+c]])

def plane(Lx, Ly, Nx, Ny, n, d):
    """ Calculate points of a generic plane

    Arguments:
    - `Lx` : Plane Length first direction
    - `Ly` : Plane Length second direction
    - `Nx` : Number of points, first direction
    - `Ny` : Number of points, second direction
    - `n`  : Plane orientation, normal vector
    - `d`  : distance from the origin
    """

    x = np.linspace(-Lx/2, Lx/2, Nx)
    y = np.linspace(-Ly/2, Ly/2, Ny)
    # Create the mesh grid, of a XY plane sitting on the orgin
    X, Y = np.meshgrid(x, y)
    Z = np.zeros([Nx, Ny])
    n0 = np.array([0, 0, 1])

    # Rotate plane to the given normal vector
    if any(n0 != n):
        costheta = np.dot(n0, n)/(np.linalg.norm(n0)*np.linalg.norm(n))
        axis = np.cross(n0, n)/np.linalg.norm(np.cross(n0, n))
        rotMatrix = rotmatrix(axis, costheta)
        XYZ = np.vstack([X.flatten(), Y.flatten(), Z.flatten()])
        X, Y, Z = np.array(rotMatrix*XYZ).reshape(3, Nx, Ny)

    eps = 0.000000001
    dVec = d #abs((n/np.linalg.norm(n)))*d#np.array([abs(n[i])/np.linalg.norm(n)*val if abs(n[i]) > eps else val for i, val in enumerate(d)]) #
    X, Y, Z = X+dVec[0], Y+dVec[1], Z+dVec[2]
    return X, Y, Z

def build_proxy_graph(proxy_n_dist, proxy_n_indices):
    G = nx.Graph()

    for distance_list, neighbor_list in zip(proxy_n_dist, proxy_n_indices):
        # first element is always point
        current_node = neighbor_list[0]
        neighbor_list = neighbor_list[1:]
        distance_list = distance_list[1:]
        for neighbor, dist in zip(neighbor_list, distance_list):
            G.add_edge(current_node, neighbor, weight=dist)
    return G

def get_plane_points(normal_vec, initial_point, min_range=-10, max_range=10, steps=1000):
    steps_for_plane = np.linspace(min_range, max_range, steps)
    xx, yy = np.meshgrid(steps_for_plane, steps_for_plane)
    d = -initial_point.dot(normal_vec)
    eps = 0.000000001
    if abs(normal_vec[2]) < eps and abs(normal_vec[1]) > eps:
        zz = (-xx*normal_vec[2] - yy*normal_vec[0] - d)/normal_vec[1]
    else:
        zz = (-xx*normal_vec[0] - yy*normal_vec[1] - d)/normal_vec[2]
    return xx, yy, zz

# def plot_tangent_plane_at_point(pointset, flattened_points, node, normal_vec):
#     ax = get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
#     node_loc = flattened_points[node]
#     print("Node loc: {}".format(node_loc))
#     xx, yy, zz = plane(10, 10, 500, 500, normal_vec, node_loc)
#     # xx, yy, zz = get_plane_points(normal_vec, node_loc)
#     print("Normal Vec: {}".format(normal_vec))
#     ax.plot_surface(X=xx, Y=yy, Z=zz)
#     ax.plot([node_loc[0]], [node_loc[1]], [node_loc[2]], markerfacecolor='k', markeredgecolor='k', marker='o', markersize=10)
#     plt.show()

def generate_tangent_spaces(proxy_graph, flattened_points):
    # This depth should gaurantee at least 16 neighbors
    tangent_spaces = {}
    for node in proxy_graph.nodes():
        neighbors = list(nx.neighbors(proxy_graph, node))
        node_point = flattened_points[node]
        zero_mean_mat = np.zeros((len(neighbors)+1, len(node_point)))
        for i, neighbor in enumerate(neighbors):
            zero_mean_mat[i] = flattened_points[neighbor]
        zero_mean_mat[-1] = node_point

        zero_mean_mat = zero_mean_mat - np.mean(zero_mean_mat, axis=0)
        u, s, v = svd(zero_mean_mat.T)
        # smat = np.zeros(u.shape[0], v.shape[0])
        # smat[:s.shape[0], :s.shape[0]] = np.diag(s)
        tangent_spaces[node] = u
    return tangent_spaces

def geodesic_single_path_dijkstra(flattened_points, proximity_graph, tangent_frames, start, end):
    # short circuit
    if start == end:
        return []
    # Create min priority queue
    minheap = []
    pred = {}
    dist = defaultdict(lambda: 1.0e+100)
    # for i, point in enumerate(flattened_points):
    R = {}
    t_dist = {}
    geo_dist = {}
    R[start] = np.eye(3)
    t_dist[start] = np.ones((3,))
    dist[start] = 0
    start_vector = flattened_points[start]
    for neighbor in nx.neighbors(proxy_graph, start):
        pred[neighbor] = start
        dist[neighbor] = np.linalg.norm(start_vector - flattened_points[neighbor])
        heapq.heappush(minheap, (dist[neighbor], neighbor))
    while minheap:
        r_dist, r_ind = heapq.heappop(minheap)
        if r_ind == end:
            break
        q_ind = pred[r_ind]
        u, s, v = svd(tangent_frames[q_ind].T*tangent_frames[r_ind])
        R[r_ind] = np.dot(R[q_ind], u * v.T)
        t_dist[r_ind] = t_dist[q_ind]+np.dot(R[q_ind], tangent_frames[q_ind].T * (r_dist - dist[q_ind]))
        geo_dist[r_ind] = np.linalg.norm(t_dist[r_ind])
        for neighbor in nx.neighbors(proxy_graph, r_ind):
            temp_dist = dist[r_ind] + np.linalg.norm(flattened_points[neighbor] - flattened_points[r_ind])
            if temp_dist < dist[neighbor]:
                dist[neighbor] = temp_dist
                pred[neighbor] = r_ind
                heapq.heappush(minheap, (dist[neighbor], neighbor))
    # found ending index, now loop through preds for path
    current_ind = end
    node_path = [end]
    while current_ind != start:
        node_path.append(pred[current_ind])
        current_ind = pred[current_ind]

    return node_path

def plot_path_on_surface(pointset, flattened_points, path):
    # ax = get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
    # ax.plot(points_in_path[:, 0], points_in_path[:, 1], points_in_path[:, 2], linewidth=10.0)
    # plt.show()
    get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
    points_in_path = flattened_points[path]
    mlab.plot3d(points_in_path[:, 0], points_in_path[:, 1], points_in_path[:, 2] *.1)
    mlab.show()

"""
    True geodesic of graph.
    Build proximity graph
    Find tangent space using geodisic neighborhood at each point in graph
    Parallel transport vectors between tangent space points
    Use this as your distance metric
    Dijkstra's Algorithm
"""
if __name__ == "__main__":
    x, y, z = surface_squares(-5, 5, -5, 5, 500)
    # plot_meshgrid(x, y, z)
    pointset = np.stack([x, y, z], axis=2)
    proxy_graph_num_neighbors = 16
    flattened_points = pointset.reshape(pointset.shape[0]*pointset.shape[1], pointset.shape[2])
    flattened_points = flattened_points
    proxy_n_dist, proxy_n_indices = get_knn(flattened_points, proxy_graph_num_neighbors)
    # Generate a proximity graph using proxy_graph_num_neighbors
    # Nodes = number of points, max # of edges = number of points * num_neighbors
    proxy_graph = build_proxy_graph(proxy_n_dist, proxy_n_indices)
    # Now, using the geodesic_num_neighbors, get geodesic neighborshood for tangent space construction
    tangent_spaces = generate_tangent_spaces(proxy_graph, flattened_points)
    node_to_use = 2968
    # 3rd vector of tangent space is normal to plane
    # plot_tangent_plane_at_point(pointset, flattened_points, node_to_use, tangent_spaces[node_to_use][:, 2])
    path = geodesic_single_path_dijkstra(flattened_points, proxy_graph, tangent_spaces, 250, 249750)
    plot_path_on_surface(pointset, flattened_points, path)

Note that I installed and set up mayavi to get a decent output image (matplotlib doesn't have real 3d rendering and consequently, its plots suck).  I did however leave the matplotlib code in if you want to use it.  If you do, just remove the scaling by .1 in the path plotter and uncomment the plotting code. Anyways, here's an example image for z=x^2+y^2.  The white line is the geodesic path:

You could also fairly easily adjust this to return all the pairwise geodesic distances between nodes from dijkstra's algorithm (look in the appendix of the paper to see the minor modifications you'll need to do this).  Then you could draw whatever lines you want on your surface.


来源: stackexchange

问:
bubble sort recursively without loops
is it even possible to do bubble sort without any global/static variables, only one array and without a loops? I am wondering is it possible and if so, is it hard?

my function would only take array with numbers and size of it.

int bubble_sort_asc_rec(int tab[], int size)

答:
```<code>#include <stdio.h>

int bubble_sort_pass(int array[], int size)
{
    /* We return the number of swaps in this iteration. */
    int swaps = 0;

    /* If the array contains 0 or 1 elements, it is already sorted. */
    if (size < 2) {
        return swaps;
    }

    /* Array contains at least 2 elements. */
    if (array[0] > array[1]) {
        /* First two elements are in wrong order. We need to swap them. */
        printf("Swap value %d with value %d\n", array[0], array[1]);
        int temp = array[0];
        array[0] = array[1];
        array[1] = temp;
        swaps += 1;
    }

    /* Recursively bubble sort the array starting at the 2nd element. */
    swaps += bubble_sort_pass(array + 1, size - 1);
    return swaps;
}

void bubble_sort(int array[], int size)
{
    /* Do one pass of bubble sorting the entire array. */
    printf("Start a bubble sort pass.\n");
    int swaps = bubble_sort_pass(array, size);

    /* If there was at least one swap, we have to do another pass. */
    if (swaps >= 1) {
        bubble_sort(array, size);
    }

}

int main()
{
    int numbers[] = {10, 8, 3, 7, 2, 1, 4, 6, 5, 9};
    int count = sizeof(numbers) / sizeof(numbers[0]);
    bubble_sort(numbers, count);
    for(int i=0; i<count; ++i) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
    }
    return 0;
}
</code>```

In C you would not normally implement bubble sort this way; instead you would use an iterative approach (i.e. loops).

This recursive implementation is strictly for instructional purposes. Some so-called "pure functional" languages do not have any loops, they only have recursion. One such language is Erlang.

Note that the recursive call to bubble_sort is the last statement in bubble_sort. This is called "tail recursion" and it allows the compiler to optimize out pushing a return address on the stack. The C compiler (likely) will not make such a tail recursion optimization, but a functional compiler (e.g. an Erlang compiler) will.

The recursive call to bubble_sort_pass in bubble_sort_pass is currently not a tail recursion call, but it could easily be changed into a tail recursion call (verify this for yourself!)

ContentLicense: CC BY-SA 4.0

Title: bubble sort recursively without loops

Body:
#include <stdio.h>

int bubble_sort_pass(int array[], int size)
{
    /* We return the number of swaps in this iteration. */
    int swaps = 0;

    /* If the array contains 0 or 1 elements, it is already sorted. */
    if (size < 2) {
        return swaps;
    }

    /* Array contains at least 2 elements. */
    if (array[0] > array[1]) {
        /* First two elements are in wrong order. We need to swap them. */
        printf("Swap value %d with value %d\n", array[0], array[1]);
        int temp = array[0];
        array[0] = array[1];
        array[1] = temp;
        swaps += 1;
    }

    /* Recursively bubble sort the array starting at the 2nd element. */
    swaps += bubble_sort_pass(array + 1, size - 1);
    return swaps;
}

void bubble_sort(int array[], int size)
{
    /* Do one pass of bubble sorting the entire array. */
    printf("Start a bubble sort pass.\n");
    int swaps = bubble_sort_pass(array, size);

    /* If there was at least one swap, we have to do another pass. */
    if (swaps >= 1) {
        bubble_sort(array, size);
    }

}

int main()
{
    int numbers[] = {10, 8, 3, 7, 2, 1, 4, 6, 5, 9};
    int count = sizeof(numbers) / sizeof(numbers[0]);
    bubble_sort(numbers, count);
    for(int i=0; i<count; ++i) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
    }
    return 0;
}

In C you would not normally implement bubble sort this way; instead you would use an iterative approach (i.e. loops).

This recursive implementation is strictly for instructional purposes. Some so-called "pure functional" languages do not have any loops, they only have recursion. One such language is Erlang.

Note that the recursive call to bubble_sort is the last statement in bubble_sort. This is called "tail recursion" and it allows the compiler to optimize out pushing a return address on the stack. The C compiler (likely) will not make such a tail recursion optimization, but a functional compiler (e.g. an Erlang compiler) will.

The recursive call to bubble_sort_pass in bubble_sort_pass is currently not a tail recursion call, but it could easily be changed into a tail recursion call (verify this for yourself!)


问:
Newbie react question: Target container is not a DOM element when calling react from html
My plan is to have a html call a react component (to test the react component, so the 1st thing i'm trying is a react class.

This is my the react file

```<code>class Hello extends React.Component {
    render() {
        return React.createElement("h1", {}, "Hello ");
    }
}

const HelloElement = React.createElement(Hello, {name: "World"});
ReactDOM.render(HelloElement, document.getElementById('mydiv'));
</code>```

This is the html file,

```<code><!DOCTYPE html>
<html>
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

<body>

<script src="Hello.js"></script>
<div id="mydiv"></div>

</body>
</html>
</code>```

But i get Error Decoder which is Target container is not a DOM element.

How to fix this pls ?

答:
Samuel already wrote it in his comment... you need to put the
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

```

As you use React.createElement instead of JSX you also don't need Babel so far.

Body:
My plan is to have a html call a react component (to test the react component, so the 1st thing i'm trying is a react class.

This is my the react file

class Hello extends React.Component {
    render() {
        return React.createElement("h1", {}, "Hello ");
    }
}

const HelloElement = React.createElement(Hello, {name: "World"});
ReactDOM.render(HelloElement, document.getElementById('mydiv'));

This is the html file,

<!DOCTYPE html>

But i get Error Decoder which is Target container is not a DOM element.

How to fix this pls ?

Body:
Samuel already wrote it in his comment... you need to put the <script src="Hello.js"> behind the div

The code executes in order, so when Hello.js runs, the div with id='mydiv' doesn't exist yet. Otherwise your code works just fine. All you need is to make use of that name prop.

class Hello extends React.Component {
  render() {
        return React.createElement("h1", {}, "Hello " + this.props.name);
  }
}
const HelloElement = React.createElement(Hello, {name: "World"});
ReactDOM.render(HelloElement, document.getElementById('mydiv'));

<!DOCTYPE html>
<html>
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<body>
<div id="mydiv"></div>
</body>
</html>

As you use React.createElement instead of JSX you also don't need Babel so far.


问:
Format-Table in PowerShell inside a ForEach
Not sure what I am doing wrong here with this,  I know it has to do with the fact that it is inside a ForEach loop.  I have tried moving the below code around.  Even with the code half and half (half in and half out of the loop does not seem to work).  Still new to PowerShell and I know that I need to variables to $table each iteration or store them some where and read them later.

```<code>foreach ($gp in $GPINFO) {

    # Code that gets the values for $gp, $aclBefore and $aclAfter is here

    $table = new-object psobject -Property @{
        GPO_Name = $gp.DisplayName
        Old_Owner = $aclBefore
        New_Owner = $aclAfter
    }
    $table | Format-Table GPO_Name,Old_Owner,New_Owner
}
</code>```

If you can help me figure out what I am doing wrong that would be great, I know that every time the ForEach gets $gp out of the $GPINFO it is running the $table stuff and that is my problem.  So instead of one continuous table I end up with multiple tables with one set of data in each.

Thanks in advance

答:
Just simply output the table after the loop completes.

```<code>$table = foreach ($gp in $GPINFO) {
    # Code that gets the values for $aclBefore and $aclAfter is here

    new-object psobject -Property @{
        GPO_Name = $gp.DisplayName
        Old_Owner = $aclBefore
        New_Owner = $aclAfter
    }
}
$table | Format-Table
</code>```

Any output from within the foreach loop will be stored in $table. Each iteration will output an object, ultimately creating an array of those objects stored in $table.

Body:
Not sure what I am doing wrong here with this,  I know it has to do with the fact that it is inside a ForEach loop.  I have tried moving the below code around.  Even with the code half and half (half in and half out of the loop does not seem to work).  Still new to PowerShell and I know that I need to variables to $table each iteration or store them some where and read them later.

foreach ($gp in $GPINFO) {

    # Code that gets the values for $gp, $aclBefore and $aclAfter is here

    $table = new-object psobject -Property @{
        GPO_Name = $gp.DisplayName
        Old_Owner = $aclBefore
        New_Owner = $aclAfter
    }
    $table | Format-Table GPO_Name,Old_Owner,New_Owner
}

If you can help me figure out what I am doing wrong that would be great, I know that every time the ForEach gets $gp out of the $GPINFO it is running the $table stuff and that is my problem.  So instead of one continuous table I end up with multiple tables with one set of data in each.

Thanks in advance

Body:
Just simply output the table after the loop completes.

$table = foreach ($gp in $GPINFO) {
    # Code that gets the values for $aclBefore and $aclAfter is here

    new-object psobject -Property @{
        GPO_Name = $gp.DisplayName
        Old_Owner = $aclBefore
        New_Owner = $aclAfter
    }
}
$table | Format-Table

Any output from within the foreach loop will be stored in $table. Each iteration will output an object, ultimately creating an array of those objects stored in $table.


问:
Large number of RemoteServiceExceptions in Google's cast MediaNotificationService
In the last ~24 hours, we've seen a few thousand crashes within Google's MediaNotificationService:

```<code>Fatal Exception: android.app.RemoteServiceException
    Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{f9a4deb u0 <our package name>/com.google.android.gms.cast.framework.media.MediaNotificationService}
    android.app.ActivityThread$H.handleMessage (ActivityThread.java:1855)
    android.os.Handler.dispatchMessage (Handler.java:106)
    android.os.Looper.loop (Looper.java:214)
    android.app.ActivityThread.main (ActivityThread.java:6986)
    java.lang.reflect.Method.invoke (Method.java)
    com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
    com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)
</code>```

I've experienced similar issues when creating my own foreground service, but as this is within the cast library we have no control over it.

The Chromecast receiver is handled by a third party. We are using:

```<code>api "com.google.android.gms:play-services-cast:17.0.0"
api "com.google.android.gms:play-services-cast-framework:17.0.0"
</code>```

Potential clues:

It is happening on OnePlus, Huawei, Samsung, Google, seemingly every manufacturer (and OS level) in numbers correlating to their market share.

The crashes are occurring on different lines for different devices (e.g. the above is the Galaxy S9, the S8 crashes on line 1872 instead), so are not grouped together on Crashlytics. This suggests to me it's an OS / Google Play Services level issue.

The crashes happen across all active versions of the app, starting at the same time.

The crashes have been occurring in low numbers for months, but suddenly spiked over the weekend and show no sign of slowing down.

UPDATE: Finally managed to reproduce this. The crash happens when casting content for a long period of time with the screen locked, and then disconnecting from the device. Maybe one step closer to a solution...

答:
The fix has been released on the Android Cast SDK client library v18.0.0 (check the second item of the release notes: https://developers.google.com/cast/docs/release-notes#december-5,-2019)

The crash should be fixed once you update to v18.0.0

Body:
In the last ~24 hours, we've seen a few thousand crashes within Google's MediaNotificationService:

Fatal Exception: android.app.RemoteServiceException
    Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{f9a4deb u0 <our package name>/com.google.android.gms.cast.framework.media.MediaNotificationService}
    android.app.ActivityThread$H.handleMessage (ActivityThread.java:1855)
    android.os.Handler.dispatchMessage (Handler.java:106)
    android.os.Looper.loop (Looper.java:214)
    android.app.ActivityThread.main (ActivityThread.java:6986)
    java.lang.reflect.Method.invoke (Method.java)
    com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
    com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)

I've experienced similar issues when creating my own foreground service, but as this is within the cast library we have no control over it.

The Chromecast receiver is handled by a third party. We are using:

api "com.google.android.gms:play-services-cast:17.0.0"
api "com.google.android.gms:play-services-cast-framework:17.0.0"

Potential clues:

It is happening on OnePlus, Huawei, Samsung, Google, seemingly every manufacturer (and OS level) in numbers correlating to their market share.

The crashes are occurring on different lines for different devices (e.g. the above is the Galaxy S9, the S8 crashes on line 1872 instead), so are not grouped together on Crashlytics. This suggests to me it's an OS / Google Play Services level issue.

The crashes happen across all active versions of the app, starting at the same time.

The crashes have been occurring in low numbers for months, but suddenly spiked over the weekend and show no sign of slowing down.

UPDATE: Finally managed to reproduce this. The crash happens when casting content for a long period of time with the screen locked, and then disconnecting from the device. Maybe one step closer to a solution...


问:
PHP Variables made with foreach
I have several variables coming from an array in $POST_['array'] i wish to make some kind of loop for example foreach that makes, for every value in the variable a variable name of it and assigns the value for it.

For example if i have

```<code>$POST_['name'];
$POST_['last'];
$POST_['age'];
$POST_['sex'];
</code>```

I want the loop to create each variable from the array inside the $_POST with the name of the variable like the following:

```<code>$name = 'John';
$last = 'Doe';
$age = '32';
$sex = 'male';
</code>```

NOTE - The array is coming from a serialized jquery string that puts together all the variables and values in a form into one big string.

Is this possible?

答:
Try not to use extract() when using $_POST. You may overwrite variables you want which will result in unpredictable behaviour. It is a bad habit to get into and while is dynamic may not be the best solution.

You can do something like this:

```<code>foreach($_POST as $key => $value)
{
    switch($key)
    {
        case "name":
            $name = $value;
        break;
        case "last":
            $last = $value;
        break;
    }
}
</code>```

ContentLicense: CC BY-SA 3.0

Title: PHP Variables made with foreach

Body:
I have several variables coming from an array in $POST_['array'] i wish to make some kind of loop for example foreach that makes, for every value in the variable a variable name of it and assigns the value for it.

For example if i have

$POST_['name'];
$POST_['last'];
$POST_['age'];
$POST_['sex'];

I want the loop to create each variable from the array inside the $_POST with the name of the variable like the following:

$name = 'John';
$last = 'Doe';
$age = '32';
$sex = 'male';

NOTE - The array is coming from a serialized jquery string that puts together all the variables and values in a form into one big string.

Is this possible?

Body:
Try not to use extract() when using $_POST. You may overwrite variables you want which will result in unpredictable behaviour. It is a bad habit to get into and while is dynamic may not be the best solution.

You can do something like this:

foreach($_POST as $key => $value)
{
    switch($key)
    {
        case "name":
            $name = $value;
        break;
        case "last":
            $last = $value;
        break;
    }
}


问:
Ag-grid custom tooltip with functional component
I am looking at ag-Grid's example on creating a custom tooltip.

```<code>import React, {Component} from 'react';

export default class CustomTooltip extends Component {
    getReactContainerClasses() {
        return ['custom-tooltip'];
    }

    render() {
        const data = this.props.api.getDisplayedRowAtIndex(this.props.rowIndex).data;
        return (
            <div className="custom-tooltip" style={{backgroundColor: this.props.color || 'white'}}>
                <p><span>{data.athlete}</span></p>
                <p><span>Country: </span> {data.country}</p>
                <p><span>Total: </span> {data.total}</p>
            </div>
        );
    }
}
</code>```

According to ag-Grid's react component page, "If you wish to override the style of this div you can either provide an implementation of the ag-react-container class, or via the getReactContainerStyle or getReactContainerClasses callbacks on the React component:"

How would I go about creating a custom tooltip using a functional component? I am not sure how I would provide an implementation of the getReactContainerClasses callback.

答:
You won't be able to have the public function getReactContainerClasses in a functional component, you'd need to write a class component.  If you want to write a functional component, just set the CSS class directly on the container DOM element, similarly to their vanilla JS example.  Below is a functional tooltip example which sets the class custom-tooltip.

```<code>import React, {Component} from 'react';

export const FunctionalCustomTooltip = (props) => {
    props.reactContainer.classList.add('custom-tooltip');

    const data = props.api.getDisplayedRowAtIndex(props.rowIndex).data;
    return (
        <div className="custom-tooltip" style={{backgroundColor: props.color || 'white'}}>
            <p><span>{data.athlete}</span></p>
            <p><span>Country: </span> {data.country}</p>
            <p><span>Total: </span> {data.total}</p>
        </div>
    );
};

</code>```

Fully working example:
https://plnkr.co/edit/WHEgtw0YVia1BVP4SVO8?p=preview

Body:
I am looking at ag-Grid's example on creating a custom tooltip.

import React, {Component} from 'react';

export default class CustomTooltip extends Component {
    getReactContainerClasses() {
        return ['custom-tooltip'];
    }

    render() {
        const data = this.props.api.getDisplayedRowAtIndex(this.props.rowIndex).data;
        return (

{data.athlete}

Country:  {data.country}

Total:  {data.total}

        );
    }
}

According to ag-Grid's react component page, "If you wish to override the style of this div you can either provide an implementation of the ag-react-container class, or via the getReactContainerStyle or getReactContainerClasses callbacks on the React component:"

How would I go about creating a custom tooltip using a functional component? I am not sure how I would provide an implementation of the getReactContainerClasses callback.

Body:
You won't be able to have the public function getReactContainerClasses in a functional component, you'd need to write a class component.  If you want to write a functional component, just set the CSS class directly on the container DOM element, similarly to their vanilla JS example.  Below is a functional tooltip example which sets the class custom-tooltip.

import React, {Component} from 'react';

export const FunctionalCustomTooltip = (props) => {
    props.reactContainer.classList.add('custom-tooltip');

    const data = props.api.getDisplayedRowAtIndex(props.rowIndex).data;
    return (
        <div className="custom-tooltip" style={{backgroundColor: props.color || 'white'}}>
            <p><span>{data.athlete}</span></p>
            <p><span>Country: </span> {data.country}</p>
            <p><span>Total: </span> {data.total}</p>
        </div>
    );
};

Fully working example:
https://plnkr.co/edit/WHEgtw0YVia1BVP4SVO8?p=preview


问:
Construct a query using glue but make it not a string
I am working in rstudio with a r script that reads a sql script using glue. My r script:

```<code>library(tidyverse)
library(dbplyr)
library(DBI)
library(glue)
con <- dbConnect(odbc(), "Athena")

day_from <- 7
day_to <- 30
game_name <- 'fungame'
query <- read_lines("example.sql") %>% glue_collapse(sep = "\n") %>% glue_sql(.con = con)
setosa <- dbGetQuery(con, query)
setosa %>% glimpse()
</code>```

My sql script query.sql:

```<code>/*a comment describing the query*/
with

/*
Athena installs data. After deducting the training period, get the preceding 90 days of installs for training.
*/
installs as (
select s,
       install_dt,
       split(game_name, '_')[2] as platform,
       case when country = 'United States' then 1 else 0 end as usa
from device_metrics.game_install
where year || '-' || month || '-' || day >= date_format(date_add('day', -({day_to} + 91), current_date), '%Y-%m-%d')
and year || '-' || month || '-' || day <= date_format(date_add('day', -({day_to} + 1), current_date), '%Y-%m-%d')
and regexp_like(lower(game_name), ('^(?!.*QA).*' || {game_name} || '.*')) -- excludes 'QA' devices
),

/*
Get marketing data from adx.
Full quarter of training data with at least one full day_m day cycle i.e. last full quarter + day_m days
*/
adx_min as (
select
  adx_id,
  publisher_name,
  row_number() over(partition by adx_id order by time_stamp asc) rn -- some dups, get first instance of an install
from blah_ui_dev.adxdata_match_v2
where lower(game_name) = {game_name}
and concat(yy,'-',mm,'-',dd) >= date_format(date_add('day', -({day_to} + 91), current_date), '%Y-%m-%d')
and concat(yy,'-',mm,'-',dd) <= date_format(date_add('day', -({day_to} + 1), current_date), '%Y-%m-%d')
),

/*
Dedupped installs based on earliest timestamp
*/
adx as (
select
  adx_id,
  publisher_name
from adx_min
where rn = 1
),

/*
installs and marketing dta where exists
use min/max to dedup, some cases with a single s assoociated with multiple platforms
*/
installs_base as (
select
  i.s,
  i.usa,
  min(i.install_dt) as install_dt,
  min(i.platform) as platform,
  min(a.publisher_name) as publisher_name
from installs i
left join adx a on upper(if(i.s like 'IDFV%', substr(i.s,6), i.s)) = a.adx_id
group by i.s, i.usa
),

/*
day n sessions count
*/
sessions_day_from as (
select i.s,
       count(1) as sessions_day_from,
       sum(session_length) / 1000 as sum_session_time_day_from
from installs_base i
join device_metrics.user_game_session sess on sess.s = i.s
where regexp_like(lower(sess.game_name), '^(?!.*QA).*' || {game_name} || '.*')
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(sess.activity_date, '%Y-%m-%d')) <= {day_from}
group by i.s
),

/*
day n utility
*/
utility_day_from as (
select
  u.s,
  sum(u.utility) as utility_day_from
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = {game_name}
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= {day_from}
group by u.s
),

/*
recent utility. Are users continuing to play the game more recently or did they drop off.
*/
utility_recent as (
select
  u.s,
  sum(u.utility) as recent_utility_sum
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = {game_name}
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= floor({day_from} / 2)
group by u.s
),

/*
day n spend
*/
spend_day_from as (
select
  i.s,
  sum(dr.amt) as spend_day_from
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || {game_name} || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= {day_from}
group by 1
),

/*
day m target spend (target)
*/
spend_day_to as (
select
  i.s,
  sum(dr.amt) as spend_day_to
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || {game_name} || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= {day_to}
group by 1
)

select
  i.s,
  i.install_dt,
  i.platform,
  i.usa,
  i.publisher_name,
  coalesce(sn.sessions_day_from, 0) as {glue('sessions_day_{day_from}')},
  coalesce(sn.sum_session_time_day_from, 0) as {glue('sum_session_time_day_{day_from}')},
  coalesce(un.utility_day_from, 0) as {glue('utility_day_{day_from}')},
  coalesce(spn.spend_day_from, 0) as {glue('spend_day_{day_from}')},
  round(coalesce(ru.recent_utility_sum / un.utility_day_from, 0), 2) as recent_utility_ratio,
  coalesce(spm.spend_day_to, 0) as {glue('spend_day_{day_to}')}
from installs_base i
left join sessions_day_from sn on sn.s = i.s
left join utility_day_from un on un.s = i.s
left join spend_day_from spn on spn.s = i.s
left join utility_recent ru on ru.s = i.s
left join spend_day_to spm on spm.s = i.s
</code>```

I can see what the sql looks like in the console by typing the variable name query:

```<code>query
<SQL> -- !preview conn=con

/*DDA day day_from to day day_to training data query*/
with

/*
Athena installs data. After deducting the training period, get the preceeding 90 days of installs for training.
*/
installs as (
select s,
       install_dt,
       split(game_name, '_')[2] as platform,
       case when country = 'United States' then 1 else 0 end as usa
from device_metrics.game_install
where year || '-' || month || '-' || day >= date_format(date_add('day', -(30 + 91), current_date), '%Y-%m-%d')
and year || '-' || month || '-' || day <= date_format(date_add('day', -(30 + 1), current_date), '%Y-%m-%d')
and regexp_like(lower(game_name), ('^(?!.*QA).*' || 'fungame' || '.*')) -- excludes 'QA' devices
),

/*
Get marketing data from adx.
Full quarter of training data with at least one full day_m day cycle i.e. last full quarter + day_m days
*/
adx_min as (
select
  adx_id,
  publisher_name,
  row_number() over(partition by adx_id order by time_stamp asc) rn -- some dups, get first instance of an install
from glu_ui_dev.adxdata_match_v2
where lower(game_name) = 'fungame'
and concat(yy,'-',mm,'-',dd) >= date_format(date_add('day', -(30 + 91), current_date), '%Y-%m-%d')
and concat(yy,'-',mm,'-',dd) <= date_format(date_add('day', -(30 + 1), current_date), '%Y-%m-%d')
),

/*
Dedupped installs based on earliest timestamp
*/
adx as (
select
  adx_id,
  publisher_name
from adx_min
where rn = 1
),

/*
installs and marketing dta where exists
use min/max to dedup, some cases with a single s assoociated with multiple platforms
*/
installs_base as (
select
  i.s,
  i.usa,
  min(i.install_dt) as install_dt,
  min(i.platform) as platform,
  min(a.publisher_name) as publisher_name
from installs i
left join adx a on upper(if(i.s like 'IDFV%', substr(i.s,6), i.s)) = a.adx_id
group by i.s, i.usa
),

/*
day n sessions count
*/
sessions_day_from as (
select i.s,
       count(1) as sessions_day_from,
       sum(session_length) / 1000 as sum_session_time_day_from
from installs_base i
join device_metrics.user_game_session sess on sess.s = i.s
where regexp_like(lower(sess.game_name), '^(?!.*QA).*' || 'fungame' || '.*')
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(sess.activity_date, '%Y-%m-%d')) <= 7
group by i.s
),

/*
day n utility
*/
utility_day_from as (
select
  u.s,
  sum(u.utility) as utility_day_from
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = 'fungame'
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= 7
group by u.s
),

/*
recent utility. Are users continuing to play the game more recently or did they drop off.
*/
utility_recent as (
select
  u.s,
  sum(u.utility) as recent_utility_sum
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = 'fungame'
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= floor(7 / 2)
group by u.s
),

/*
day n spend
*/
spend_day_from as (
select
  i.s,
  sum(dr.amt) as spend_day_from
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || 'fungame' || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= 7
group by 1
),

/*
day m target spend (target)
*/
spend_day_to as (
select
  i.s,
  sum(dr.amt) as spend_day_to
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || 'fungame' || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= 30
group by 1
)

select
  i.s,
  i.install_dt,
  i.platform,
  i.usa,
  i.publisher_name,
  coalesce(sn.sessions_day_from, 0) as 'sessions_day_7',
  coalesce(sn.sum_session_time_day_from, 0) as 'sum_session_time_day_7',
  coalesce(un.utility_day_from, 0) as 'utility_day_7',
  coalesce(spn.spend_day_from, 0) as 'spend_day_7',
  round(coalesce(ru.recent_utility_sum / un.utility_day_from, 0), 2) as recent_utility_ratio,
  coalesce(spm.spend_day_to, 0) as 'spend_day_30'
from installs_base i
left join sessions_day_from sn on sn.s = i.s
left join utility_day_from un on un.s = i.s
left join spend_day_from spn on spn.s = i.s
left join utility_recent ru on ru.s = i.s
left join spend_day_to spm on spm.s = i.s
</code>```

The issue I'm posting about is specifically in the very bottom select field name section. After the 'as', my field names are string quoted.

Glue returns the part after 'as' as a string. This does not seem to cause problems in this case, however, in my actual script I'm using odbc with Athena, and it doesn't like the fact that the query is of the form select field_name as 'string' as opposed to the regular select field_name as string // string is not quoted as a string

Is there a way to have glue compose this part after 'as' outwith string quotes? Desired query:

```<code><SQL>
    select
      Species as Species-setosa
    from iris
    where species = 'setosa'
</code>```

答:
One option would be to replace the ' by matching the quote on a specific location with regex lookaround.  Here, we can use str_replace_all

```<code>library(magrittr)
library(stringr)
query %>%
     str_replace_all("(?<=as )'|(?<![%])'(?=(\nfrom|,))", "") %>%
     glue_sql(.con = con)
#<SQL> select
#  Species as Species-setosa
#from iris
#where species = 'setosa'
</code>```

We match the ' that follows the word as and a space or (|) the ' the precedes either a comma (,) or nextline and from (\nfrom)

Body:
I am working in rstudio with a r script that reads a sql script using glue. My r script:

library(tidyverse)
library(dbplyr)
library(DBI)
library(glue)
con <- dbConnect(odbc(), "Athena")

day_from <- 7
day_to <- 30
game_name <- 'fungame'
query <- read_lines("example.sql") %>% glue_collapse(sep = "\n") %>% glue_sql(.con = con)
setosa <- dbGetQuery(con, query)
setosa %>% glimpse()

My sql script query.sql:

/*a comment describing the query*/
with

/*
Athena installs data. After deducting the training period, get the preceding 90 days of installs for training.
*/
installs as (
select s,
       install_dt,
       split(game_name, '_')[2] as platform,
       case when country = 'United States' then 1 else 0 end as usa
from device_metrics.game_install
where year || '-' || month || '-' || day >= date_format(date_add('day', -({day_to} + 91), current_date), '%Y-%m-%d')
and year || '-' || month || '-' || day <= date_format(date_add('day', -({day_to} + 1), current_date), '%Y-%m-%d')
and regexp_like(lower(game_name), ('^(?!.*QA).*' || {game_name} || '.*')) -- excludes 'QA' devices
),

/*
Get marketing data from adx.
Full quarter of training data with at least one full day_m day cycle i.e. last full quarter + day_m days
*/
adx_min as (
select
  adx_id,
  publisher_name,
  row_number() over(partition by adx_id order by time_stamp asc) rn -- some dups, get first instance of an install
from blah_ui_dev.adxdata_match_v2
where lower(game_name) = {game_name}
and concat(yy,'-',mm,'-',dd) >= date_format(date_add('day', -({day_to} + 91), current_date), '%Y-%m-%d')
and concat(yy,'-',mm,'-',dd) <= date_format(date_add('day', -({day_to} + 1), current_date), '%Y-%m-%d')
),

/*
Dedupped installs based on earliest timestamp
*/
adx as (
select
  adx_id,
  publisher_name
from adx_min
where rn = 1
),

/*
installs and marketing dta where exists
use min/max to dedup, some cases with a single s assoociated with multiple platforms
*/
installs_base as (
select
  i.s,
  i.usa,
  min(i.install_dt) as install_dt,
  min(i.platform) as platform,
  min(a.publisher_name) as publisher_name
from installs i
left join adx a on upper(if(i.s like 'IDFV%', substr(i.s,6), i.s)) = a.adx_id
group by i.s, i.usa
),

/*
day n sessions count
*/
sessions_day_from as (
select i.s,
       count(1) as sessions_day_from,
       sum(session_length) / 1000 as sum_session_time_day_from
from installs_base i
join device_metrics.user_game_session sess on sess.s = i.s
where regexp_like(lower(sess.game_name), '^(?!.*QA).*' || {game_name} || '.*')
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(sess.activity_date, '%Y-%m-%d')) <= {day_from}
group by i.s
),

/*
day n utility
*/
utility_day_from as (
select
  u.s,
  sum(u.utility) as utility_day_from
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = {game_name}
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= {day_from}
group by u.s
),

/*
recent utility. Are users continuing to play the game more recently or did they drop off.
*/
utility_recent as (
select
  u.s,
  sum(u.utility) as recent_utility_sum
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = {game_name}
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= floor({day_from} / 2)
group by u.s
),

/*
day n spend
*/
spend_day_from as (
select
  i.s,
  sum(dr.amt) as spend_day_from
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || {game_name} || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= {day_from}
group by 1
),

/*
day m target spend (target)
*/
spend_day_to as (
select
  i.s,
  sum(dr.amt) as spend_day_to
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || {game_name} || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= {day_to}
group by 1
)

select
  i.s,
  i.install_dt,
  i.platform,
  i.usa,
  i.publisher_name,
  coalesce(sn.sessions_day_from, 0) as {glue('sessions_day_{day_from}')},
  coalesce(sn.sum_session_time_day_from, 0) as {glue('sum_session_time_day_{day_from}')},
  coalesce(un.utility_day_from, 0) as {glue('utility_day_{day_from}')},
  coalesce(spn.spend_day_from, 0) as {glue('spend_day_{day_from}')},
  round(coalesce(ru.recent_utility_sum / un.utility_day_from, 0), 2) as recent_utility_ratio,
  coalesce(spm.spend_day_to, 0) as {glue('spend_day_{day_to}')}
from installs_base i
left join sessions_day_from sn on sn.s = i.s
left join utility_day_from un on un.s = i.s
left join spend_day_from spn on spn.s = i.s
left join utility_recent ru on ru.s = i.s
left join spend_day_to spm on spm.s = i.s

I can see what the sql looks like in the console by typing the variable name query:

query
<SQL> -- !preview conn=con

/*DDA day day_from to day day_to training data query*/
with

/*
Athena installs data. After deducting the training period, get the preceeding 90 days of installs for training.
*/
installs as (
select s,
       install_dt,
       split(game_name, '_')[2] as platform,
       case when country = 'United States' then 1 else 0 end as usa
from device_metrics.game_install
where year || '-' || month || '-' || day >= date_format(date_add('day', -(30 + 91), current_date), '%Y-%m-%d')
and year || '-' || month || '-' || day <= date_format(date_add('day', -(30 + 1), current_date), '%Y-%m-%d')
and regexp_like(lower(game_name), ('^(?!.*QA).*' || 'fungame' || '.*')) -- excludes 'QA' devices
),

/*
Get marketing data from adx.
Full quarter of training data with at least one full day_m day cycle i.e. last full quarter + day_m days
*/
adx_min as (
select
  adx_id,
  publisher_name,
  row_number() over(partition by adx_id order by time_stamp asc) rn -- some dups, get first instance of an install
from glu_ui_dev.adxdata_match_v2
where lower(game_name) = 'fungame'
and concat(yy,'-',mm,'-',dd) >= date_format(date_add('day', -(30 + 91), current_date), '%Y-%m-%d')
and concat(yy,'-',mm,'-',dd) <= date_format(date_add('day', -(30 + 1), current_date), '%Y-%m-%d')
),

/*
Dedupped installs based on earliest timestamp
*/
adx as (
select
  adx_id,
  publisher_name
from adx_min
where rn = 1
),

/*
installs and marketing dta where exists
use min/max to dedup, some cases with a single s assoociated with multiple platforms
*/
installs_base as (
select
  i.s,
  i.usa,
  min(i.install_dt) as install_dt,
  min(i.platform) as platform,
  min(a.publisher_name) as publisher_name
from installs i
left join adx a on upper(if(i.s like 'IDFV%', substr(i.s,6), i.s)) = a.adx_id
group by i.s, i.usa
),

/*
day n sessions count
*/
sessions_day_from as (
select i.s,
       count(1) as sessions_day_from,
       sum(session_length) / 1000 as sum_session_time_day_from
from installs_base i
join device_metrics.user_game_session sess on sess.s = i.s
where regexp_like(lower(sess.game_name), '^(?!.*QA).*' || 'fungame' || '.*')
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(sess.activity_date, '%Y-%m-%d')) <= 7
group by i.s
),

/*
day n utility
*/
utility_day_from as (
select
  u.s,
  sum(u.utility) as utility_day_from
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = 'fungame'
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= 7
group by u.s
),

/*
recent utility. Are users continuing to play the game more recently or did they drop off.
*/
utility_recent as (
select
  u.s,
  sum(u.utility) as recent_utility_sum
from installs_base i
join adhoc.device_sessions_daily u on u.s = i.s
where lower(u.game_base) = 'fungame'
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), u.activity_date) <= floor(7 / 2)
group by u.s
),

/*
day n spend
*/
spend_day_from as (
select
  i.s,
  sum(dr.amt) as spend_day_from
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || 'fungame' || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= 7
group by 1
),

/*
day m target spend (target)
*/
spend_day_to as (
select
  i.s,
  sum(dr.amt) as spend_day_to
from device_metrics.daily_revenue dr
join installs_base i on i.s = dr.s
where coalesce(channel,'IAP') = 'IAP'
and regexp_like(lower(game_name), ('^(?!.*QA).*' || 'fungame' || '.*')) -- excludes 'QA' devices
and date_diff('day', date_parse(i.install_dt, '%Y-%m-%d'), date_parse(dr.activity_date, '%Y-%m-%d')) <= 30
group by 1
)

select
  i.s,
  i.install_dt,
  i.platform,
  i.usa,
  i.publisher_name,
  coalesce(sn.sessions_day_from, 0) as 'sessions_day_7',
  coalesce(sn.sum_session_time_day_from, 0) as 'sum_session_time_day_7',
  coalesce(un.utility_day_from, 0) as 'utility_day_7',
  coalesce(spn.spend_day_from, 0) as 'spend_day_7',
  round(coalesce(ru.recent_utility_sum / un.utility_day_from, 0), 2) as recent_utility_ratio,
  coalesce(spm.spend_day_to, 0) as 'spend_day_30'
from installs_base i
left join sessions_day_from sn on sn.s = i.s
left join utility_day_from un on un.s = i.s
left join spend_day_from spn on spn.s = i.s
left join utility_recent ru on ru.s = i.s
left join spend_day_to spm on spm.s = i.s

The issue I'm posting about is specifically in the very bottom select field name section. After the 'as', my field names are string quoted.

Glue returns the part after 'as' as a string. This does not seem to cause problems in this case, however, in my actual script I'm using odbc with Athena, and it doesn't like the fact that the query is of the form select field_name as 'string' as opposed to the regular select field_name as string // string is not quoted as a string

Is there a way to have glue compose this part after 'as' outwith string quotes? Desired query:

<SQL>
    select
      Species as Species-setosa
    from iris
    where species = 'setosa'

Body:
One option would be to replace the ' by matching the quote on a specific location with regex lookaround.  Here, we can use str_replace_all

library(magrittr)
library(stringr)
query %>%
     str_replace_all("(?<=as )'|(?<![%])'(?=(\nfrom|,))", "") %>%
     glue_sql(.con = con)
#<SQL> select
#  Species as Species-setosa
#from iris
#where species = 'setosa'

We match the ' that follows the word as and a space or (|) the ' the precedes either a comma (,) or nextline and from (\nfrom)


问:
Plot scatter chart by using loop command
the main body of the programming is shown as below:

```<code>ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(1).Name = "='7G'!$A$2"
ActiveChart.FullSeriesCollection(1).XValues = "='7G'!$B$2:$B$67"
ActiveChart.FullSeriesCollection(1).Values = "='7G'!$N$2:$N$67"
ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(2).Name = "='7G'!$A$68"
ActiveChart.FullSeriesCollection(2).XValues = "='7G'!$B$68:$B$133"
ActiveChart.FullSeriesCollection(2).Values = "='7G'!$N$68:$N$133"
ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(3).Name = "='7G'!$A$134"
ActiveChart.FullSeriesCollection(3).XValues = "='7G'!$B$134:$B199"
ActiveChart.FullSeriesCollection(3).Values = "='7G'!$N$134:$N$199"
ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(4).Name = "='7G'!$A$200"
ActiveChart.FullSeriesCollection(4).XValues = "='7G'!$B$200:$B$265"
ActiveChart.FullSeriesCollection(4).Values = "='7G'!$N$200:$N$265"
</code>```

mainly, need to plot 50 curves with above script in Excel.  Wonder if there is a way to simplify above script by using a loop statement since all series there have a formula to repeat.

答:
Untested:</p>

```<code>Dim i, rowOff As Long
Dim cht As Chart

Set cht = ActiveChart

For i = 1 To 50
    rowOff = (i - 1) * 66 'calculate offset for this set
    With cht.SeriesCollection.NewSeries
        .Name = Sheets("7G").Range("a2").Offset(rowOff, 0).Value
        .XValues = Sheets("7G").Range("B2:B67").Offset(rowOff, 0).Value
        .Values = Sheets("7G").Range("N2:N67").Offset(rowOff, 0).Value
    End With
Next i
</code>```

Body:
the main body of the programming is shown as below:

ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(1).Name = "='7G'!$A$2"
ActiveChart.FullSeriesCollection(1).XValues = "='7G'!$B$2:$B$67"
ActiveChart.FullSeriesCollection(1).Values = "='7G'!$N$2:$N$67"
ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(2).Name = "='7G'!$A$68"
ActiveChart.FullSeriesCollection(2).XValues = "='7G'!$B$68:$B$133"
ActiveChart.FullSeriesCollection(2).Values = "='7G'!$N$68:$N$133"
ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(3).Name = "='7G'!$A$134"
ActiveChart.FullSeriesCollection(3).XValues = "='7G'!$B$134:$B199"
ActiveChart.FullSeriesCollection(3).Values = "='7G'!$N$134:$N$199"
ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(4).Name = "='7G'!$A$200"
ActiveChart.FullSeriesCollection(4).XValues = "='7G'!$B$200:$B$265"
ActiveChart.FullSeriesCollection(4).Values = "='7G'!$N$200:$N$265"

mainly, need to plot 50 curves with above script in Excel.  Wonder if there is a way to simplify above script by using a loop statement since all series there have a formula to repeat.

Body:
Untested:

Dim i, rowOff As Long
Dim cht As Chart

Set cht = ActiveChart

For i = 1 To 50
    rowOff = (i - 1) * 66 'calculate offset for this set
    With cht.SeriesCollection.NewSeries
        .Name = Sheets("7G").Range("a2").Offset(rowOff, 0).Value
        .XValues = Sheets("7G").Range("B2:B67").Offset(rowOff, 0).Value
        .Values = Sheets("7G").Range("N2:N67").Offset(rowOff, 0).Value
    End With
Next i


问:
Java: compile-time resolution and "most specific method" as it applies to variable arity
Could someone help me understand section 15.12.2.5 of the JLS re: most specific method?

(bludgeoned cut&paste from JLS follows)

In addition, one variable arity member method named m is more specific than another variable arity member method of the same name if either:

One member method has n parameters and the other has k parameters, where n >= k. The types of the parameters of the first member method are T1, . . . , Tn-1 , Tn[], the types of the parameters of the other method are U1, . . . , Uk-1, Uk[]. If the second method is generic then let R1 ... Rp p1, be its formal type parameters, let Bl be the declared bound of Rl, 1lp, let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ti << Ui,1ik-1, Ti << Uk, kin and let Si = Ui[R1 = A1, ..., Rp = Ap] 1ik; otherwise let Si = Ui, 1ik. Then:
      for all j from 1 to k-1, Tj <: Sj, and,
      for all j from k to n, Tj <: Sk, and,
      If the second method is a generic method as described above then Al <: Bl[R1 = A1, ..., Rp = Ap], 1lp.

One member method has k parameters and the other has n parameters, where n >= k. The types of the parameters of the first method are U1, . . . , Uk-1, Uk[], the types of the parameters of the other method are T1, . . ., Tn-1, Tn[]. If the second method is generic then let R1 ... Rp p1, be its formal type parameters, let Bl be the declared bound of Rl, 1lp, let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ui << Ti, 1ik-1, Uk << Ti, kin and let Si = Ti[R1 = A1, ..., Rp = Ap] 1in; otherwise let Si = Ti, 1in. Then:
      for all j from 1 to k-1 , Uj <: Sj, and,
      for all j from k to n , Uk <: Sj, and,
      If the second method is a generic method as described above then Al <: Bl[R1 = A1, ..., Rp = Ap], 1lp.

Ignoring the issue generics, does this mean varargs is more important than subtyping, or subtyping is more important than varargs, when deciding whether one method is more specific than another? I can't figure it out.

Concrete example: Which of the following compute() methods is "more specific" according to the JLS?

```<code>package com.example.test.reflect;

class JLS15Test
{
    int compute(Object o1, Object o2, Object... others) { return 1; }
    int compute(String s1, Object... others)            { return 2; }

    public static void main(String[] args)
    {
        JLS15Test y = new JLS15Test();
        System.out.println(y.compute(y,y,y));
        System.out.println(y.compute("hi",y,y));
    }
}
</code>```

I can't figure out which is "more specific"; the output prints

```<code>1
2
</code>```

I'm confused how to interpret the results. When the first argument was a String, the compiler picked the method with the more specific subtype. When the first argument was an Object, the compiler picked the method with the fewer number of optional varargs.

NOTE: If you are not reading this section of the JLS, and you are giving an answer that depends on the types of the arguments, you are not helping me. If you carefully read the JLS, other than the parts relating to generics, the definition of "more specific" depends on the declared arguments, not on the actual arguments -- this comes into play in other parts of the JLS (can't find it at the moment).

e.g. for fixed arity methods, compute(String s) would be more specific than compute(Object o). But I'm trying to understand the relevant section of the JLS re: variable arity methods.

答: The second compute call prints 2 because the literal "hi" is known to be a String at compile time, so the compiler choses the second method signature because String is more specific than Object.</p>

Body:
Could someone help me understand section 15.12.2.5 of the JLS re: most specific method?

(bludgeoned cut&paste from JLS follows)

In addition, one variable arity member method named m is more specific than another variable arity member method of the same name if either:

One member method has n parameters and the other has k parameters, where n >= k. The types of the parameters of the first member method are T1, . . . , Tn-1 , Tn[], the types of the parameters of the other method are U1, . . . , Uk-1, Uk[]. If the second method is generic then let R1 ... Rp p1, be its formal type parameters, let Bl be the declared bound of Rl, 1lp, let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ti << Ui,1ik-1, Ti << Uk, kin and let Si = Ui[R1 = A1, ..., Rp = Ap] 1ik; otherwise let Si = Ui, 1ik. Then:
      for all j from 1 to k-1, Tj <: Sj, and,
      for all j from k to n, Tj <: Sk, and,
      If the second method is a generic method as described above then Al <: Bl[R1 = A1, ..., Rp = Ap], 1lp.

One member method has k parameters and the other has n parameters, where n >= k. The types of the parameters of the first method are U1, . . . , Uk-1, Uk[], the types of the parameters of the other method are T1, . . ., Tn-1, Tn[]. If the second method is generic then let R1 ... Rp p1, be its formal type parameters, let Bl be the declared bound of Rl, 1lp, let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ui << Ti, 1ik-1, Uk << Ti, kin and let Si = Ti[R1 = A1, ..., Rp = Ap] 1in; otherwise let Si = Ti, 1in. Then:
      for all j from 1 to k-1 , Uj <: Sj, and,
      for all j from k to n , Uk <: Sj, and,
      If the second method is a generic method as described above then Al <: Bl[R1 = A1, ..., Rp = Ap], 1lp.

Ignoring the issue generics, does this mean varargs is more important than subtyping, or subtyping is more important than varargs, when deciding whether one method is more specific than another? I can't figure it out.

Concrete example: Which of the following compute() methods is "more specific" according to the JLS?

package com.example.test.reflect;

class JLS15Test
{
    int compute(Object o1, Object o2, Object... others) { return 1; }
    int compute(String s1, Object... others)            { return 2; }

    public static void main(String[] args)
    {
        JLS15Test y = new JLS15Test();
        System.out.println(y.compute(y,y,y));
        System.out.println(y.compute("hi",y,y));
    }
}

I can't figure out which is "more specific"; the output prints

1
2

I'm confused how to interpret the results. When the first argument was a String, the compiler picked the method with the more specific subtype. When the first argument was an Object, the compiler picked the method with the fewer number of optional varargs.

NOTE: If you are not reading this section of the JLS, and you are giving an answer that depends on the types of the arguments, you are not helping me. If you carefully read the JLS, other than the parts relating to generics, the definition of "more specific" depends on the declared arguments, not on the actual arguments -- this comes into play in other parts of the JLS (can't find it at the moment).

e.g. for fixed arity methods, compute(String s) would be more specific than compute(Object o). But I'm trying to understand the relevant section of the JLS re: variable arity methods.


问:
Css gradient where color values are derived from element props
I'm rendering data cards for Pokemon. Each pokemon type has an associated color. Fire is red, water is blue, grass is green. Some pokemon have one type, some pokemon have 2 types.

I want the background color to be either solid if the pokemon has one type, but the tricky part is I want the background to be a gradient if the pokemon has two types. For example, a Fire pokemon would have a solid red background, whereas a water/grass pokemon would be a blue/green gradient.

There are 18 pokemon types. For a total of 324 combinations. I know I could hardcode classes for each combination .fire-ice{colors} but there has to be a better solution.

How can achieve this programatically? I'm using sass and react

答:
You can do this by setting the style (CSS) on your element dynamically based on the props passed. You just need a mapping from the Pokemon type to a color, and then just check if there is only one type or two, and use that to either set a background-color or background-image (using linear gradient).

For example:

const POKEMON_TYPES = {
    FIRE: "Fire",
    WATER: "Water",
    GRASS: "Grass",
}

const POKEMON_TYPE_COLORS = {
    [POKEMON_TYPES.FIRE]: "#ff0000",
    [POKEMON_TYPES.WATER]: "#0000ff",
    [POKEMON_TYPES.GRASS]: "#00ff00",
}

const App = () => {
    return (

            <PokemonCard name="Fire Only" types={ [POKEMON_TYPES.FIRE] }/>
            <PokemonCard name="Fire And Water" types={ [POKEMON_TYPES.FIRE, POKEMON_TYPES.WATER] }/>
            <PokemonCard name="Water And Grass" types={ [POKEMON_TYPES.WATER, POKEMON_TYPES.GRASS] }/>

    )
}

const PokemonCard = props => {
    const { name, types } = props;

    let style;
    if (types.length === 2) {
        const typeColor1 = POKEMON_TYPE_COLORS[types[0]];
        const typeColor2 = POKEMON_TYPE_COLORS[types[1]];
        style = {"backgroundImage": `linear-gradient(45deg, ${typeColor1}, ${typeColor2})`};
    } else {
        const typeColor = POKEMON_TYPE_COLORS[types[0]];
        style = {"backgroundColor": typeColor};
    }

    return (

            { name }

    )
}

ReactDOM.render(<App/>, document.querySelector("#root"));```
<pre class="snippet-code-css lang-css prettyprint-override"><code>.pokemon-card {
    color: #fff;
    width: 10rem;
    height: 5rem;
    padding: .75rem 1rem;
    margin-bottom: 1rem;
}</code>```

```

You may want to add handling to sort the types first so that each combination is always displayed the same (no flips). Also you might consider using type and types as separate props (not sure how you have that set up currently), but it should be clear how to make that change.

You may also want to set the text color differently depending on the background color, this post may be helpful for you: https://stackoverflow.com/a/3943023/8068625.

Body:
You can do this by setting the style (CSS) on your element dynamically based on the props passed. You just need a mapping from the Pokemon type to a color, and then just check if there is only one type or two, and use that to either set a background-color or background-image (using linear gradient).

For example:

const POKEMON_TYPES = {
    FIRE: "Fire",
    WATER: "Water",
    GRASS: "Grass",
}

const POKEMON_TYPE_COLORS = {
    [POKEMON_TYPES.FIRE]: "#ff0000",
    [POKEMON_TYPES.WATER]: "#0000ff",
    [POKEMON_TYPES.GRASS]: "#00ff00",
}

const App = () => {
    return (
        <div>
            <PokemonCard name="Fire Only" types={ [POKEMON_TYPES.FIRE] }/>
            <PokemonCard name="Fire And Water" types={ [POKEMON_TYPES.FIRE, POKEMON_TYPES.WATER] }/>
            <PokemonCard name="Water And Grass" types={ [POKEMON_TYPES.WATER, POKEMON_TYPES.GRASS] }/>
        </div>
    )
}

const PokemonCard = props => {
    const { name, types } = props;

    let style;
    if (types.length === 2) {
        const typeColor1 = POKEMON_TYPE_COLORS[types[0]];
        const typeColor2 = POKEMON_TYPE_COLORS[types[1]];
        style = {"backgroundImage": `linear-gradient(45deg, ${typeColor1}, ${typeColor2})`};
    } else {
        const typeColor = POKEMON_TYPE_COLORS[types[0]];
        style = {"backgroundColor": typeColor};
    }

    return (
        <div className="pokemon-card" style={ style }>
            { name }
        </div>
    )
}

ReactDOM.render(<App/>, document.querySelector("#root"));

.pokemon-card {
    color: #fff;
    width: 10rem;
    height: 5rem;
    padding: .75rem 1rem;
    margin-bottom: 1rem;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"/>

You may want to add handling to sort the types first so that each combination is always displayed the same (no flips). Also you might consider using type and types as separate props (not sure how you have that set up currently), but it should be clear how to make that change.

You may also want to set the text color differently depending on the background color, this post may be helpful for you: https://stackoverflow.com/a/3943023/8068625.


问:
Do NOT remove spaces in a variable
I'm creating a variable in a bash script, to pull a field from an sql database. There might be spaces in there, and I want to see that.

```<code>SIDE_NBR=$($CMD_CONNX "select side_nbr from setup" | awk 'NR==6{print$1}'|tr -d '\r'
</code>```

I need the tr -d '\r' as it comes in with a carriage return. However, this field might be

```<code>"    4"
</code>```

or

```<code>"4"
</code>```

I want to see those spaces so when i call my variable later, i can

```<code>log "Side_nbr=\"$SIDE_NBR\""
</code>```

And in my log would display "    4" or "4"

答:
Probably a bit more than you need for this one particular case, but something that may come in handy down the road if you plan on pulling SQL query results into your shell script ...

Consider wrapping the (database) field value in a pair of delimiters that do not show up in the field; examples could include a pipe (|), period (.), or even a multi-character delimiter that increases the chance of not finding a match in the (database) field (eg, .|.).

You can then have awk use the same delimiter for splitting fields.

You can go a bit further and flag just the data row for selection by providing a field to grep/pattern-match on (eg, the string 'GREPME').  This eliminates the need to know in advance the line number(s) that contain your data.

Let's start with the SQL query and we'll use a pipe (|) as our delimiter:

```<code>$CMD_CONNX "select 'GREP'||'ME','|'||side_nbr||'|' from setup"
</code>```

NOTE: To keep from matching a column header of 'GREPME' we need to split the string into 2 parts thus ensuring only the data row contains the string 'GREPME'.

NOTE: Depending on the RDBMS and column datatype it may be necessary to convert the column to a (var)char before appending the delimiter on the front/back of the column.

Let's assume your complete output looks something like:

```<code>Connecting to database xyz ...
Connect as myuser/***** ...

GREP||ME    |||side_nbr|||
=========== ==================
GREPME      |   4|
(1 row affected)
</code>```

One awk solution to parse out the desired data:

```<code>awk -F"|" '/GREPME/ {print $2}'
</code>```

Pulling it all together:

```<code>SIDE_NBR=$($CMD_CONNX "select 'GREP'||'ME','|'||side_nbr||'|' from setup" | awk -F"|" '/GREPME/ {print $2}')
</code>```

It gets a bit more interesting if your SQL query tool provides a non-white-space column delimiter for the result set (eg, a pipe (|)):

```<code>|Column1|Column2      |
|=======|=============|
|ABC    |some data    |
|DEF    |and more data|
</code>```

Parsing this output has issues because of the 'extra' white space for the first column due to the column header being wider than the data, and for the first row/second column due to the second row/second column value being wider.

There are several ways to get around this ... one example using an ampersand (&) as the delimiter:

```<code>select 'GREP'||'ME&'||Column1||'&'||Column2||'&' from mytable

|GREP||ME&||Column1||&||Column2||&|
|=================================|
|GREPME&ABC&some data&            |
|GREPME&DEF&and more data&        |
</code>```

Which could be parsed with awk like such:

```<code><select_query> ... | awk -F"&" '/GREPME/ {print $2,$3)' | while read -r col1 col2; do ...
</code>```

Anyhoo, lots of ways to slice-n-dice this to make it easier to parse a SQL result set into something that can be used in a shell script.

The (obvious) general idea is to have the select query format your data in such a way to allow for (easier) parsing by the shell script.

Title: Do NOT remove spaces in a variable

Body:
I'm creating a variable in a bash script, to pull a field from an sql database. There might be spaces in there, and I want to see that.

SIDE_NBR=$($CMD_CONNX "select side_nbr from setup" | awk 'NR==6{print$1}'|tr -d '\r'

I need the tr -d '\r' as it comes in with a carriage return. However, this field might be

"    4"

or

"4"

I want to see those spaces so when i call my variable later, i can

log "Side_nbr=\"$SIDE_NBR\""

And in my log would display "    4" or "4"

Body:
Probably a bit more than you need for this one particular case, but something that may come in handy down the road if you plan on pulling SQL query results into your shell script ...

Consider wrapping the (database) field value in a pair of delimiters that do not show up in the field; examples could include a pipe (|), period (.), or even a multi-character delimiter that increases the chance of not finding a match in the (database) field (eg, .|.).

You can then have awk use the same delimiter for splitting fields.

You can go a bit further and flag just the data row for selection by providing a field to grep/pattern-match on (eg, the string 'GREPME').  This eliminates the need to know in advance the line number(s) that contain your data.

Let's start with the SQL query and we'll use a pipe (|) as our delimiter:

$CMD_CONNX "select 'GREP'||'ME','|'||side_nbr||'|' from setup"

NOTE: To keep from matching a column header of 'GREPME' we need to split the string into 2 parts thus ensuring only the data row contains the string 'GREPME'.

NOTE: Depending on the RDBMS and column datatype it may be necessary to convert the column to a (var)char before appending the delimiter on the front/back of the column.

Let's assume your complete output looks something like:

Connecting to database xyz ...
Connect as myuser/***** ...

GREP||ME    |||side_nbr|||
=========== ==================
GREPME      |   4|
(1 row affected)

One awk solution to parse out the desired data:

awk -F"|" '/GREPME/ {print $2}'

Pulling it all together:

SIDE_NBR=$($CMD_CONNX "select 'GREP'||'ME','|'||side_nbr||'|' from setup" | awk -F"|" '/GREPME/ {print $2}')

It gets a bit more interesting if your SQL query tool provides a non-white-space column delimiter for the result set (eg, a pipe (|)):

|Column1|Column2      |
|=======|=============|
|ABC    |some data    |
|DEF    |and more data|

Parsing this output has issues because of the 'extra' white space for the first column due to the column header being wider than the data, and for the first row/second column due to the second row/second column value being wider.

There are several ways to get around this ... one example using an ampersand (&) as the delimiter:

select 'GREP'||'ME&'||Column1||'&'||Column2||'&' from mytable

|GREP||ME&||Column1||&||Column2||&|
|=================================|
|GREPME&ABC&some data&            |
|GREPME&DEF&and more data&        |

Which could be parsed with awk like such:

<select_query> ... | awk -F"&" '/GREPME/ {print $2,$3)' | while read -r col1 col2; do ...

Anyhoo, lots of ways to slice-n-dice this to make it easier to parse a SQL result set into something that can be used in a shell script.

The (obvious) general idea is to have the select query format your data in such a way to allow for (easier) parsing by the shell script.


问:
Powershell/C# XmlDocument selecting only the nodes without attributes
I'm trying to read a Visual Studio project file to select the <PropertyGroup> item that has no attributes to extract the value from the <AssemblyName> node.  The post is close to what I'm looking for but instead of not having a specific attribute I need it to have no attributes

I've tried the following in Powershell without any success.

```<code>$xml = [System.Xml.XmlDocument]::new();

$xml.Load("WebApplication4.csproj")
$ns = [System.Xml.XmlNamespaceManager]::new($xml.NameTable);
$ns.AddNamespace("prj", "http://schemas.microsoft.com/developer/msbuild/2003")

$xml.SelectNodes("//prj:PropertyGroup", $ns).Count
</code>```

Is there a way to use xpath syntax in the selectsinglenode method that would return just the one node that has no attributes?

```<code><?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
  <Import Project="..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props')" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{7EC4A73B-EA1B-4A6C-B2ED-093E3615C40E}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>WebApplication4</RootNamespace>
    <AssemblyName>WebApplication4</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <UseIISExpress>true</UseIISExpress>
    <Use64BitIISExpress />
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
    <UseGlobalApplicationHostFile />
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
</Project>
</code>```

答:
Another way to do it, with just xpath, could be:</p>

```<code> $xml.SelectNodes("//prj:PropertyGroup[count(@*)=0]", $ns)
</code>```

Body:
I'm trying to read a Visual Studio project file to select the <PropertyGroup> item that has no attributes to extract the value from the <AssemblyName> node.  The post is close to what I'm looking for but instead of not having a specific attribute I need it to have no attributes

I've tried the following in Powershell without any success.

$xml = [System.Xml.XmlDocument]::new();

$xml.Load("WebApplication4.csproj")
$ns = [System.Xml.XmlNamespaceManager]::new($xml.NameTable);
$ns.AddNamespace("prj", "http://schemas.microsoft.com/developer/msbuild/2003")

$xml.SelectNodes("//prj:PropertyGroup", $ns).Count

Is there a way to use xpath syntax in the selectsinglenode method that would return just the one node that has no attributes?

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
  <Import Project="..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props')" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{7EC4A73B-EA1B-4A6C-B2ED-093E3615C40E}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>WebApplication4</RootNamespace>
    <AssemblyName>WebApplication4</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <UseIISExpress>true</UseIISExpress>
    <Use64BitIISExpress />
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
    <UseGlobalApplicationHostFile />
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
</Project>

Body:
Another way to do it, with just xpath, could be:

 $xml.SelectNodes("//prj:PropertyGroup[count(@*)=0]", $ns)