我对函数T
引起的某种类型equivalent
有二元关系:
bool equivalent(T const& a, T const& b); // returns true if a and b are equivalent
它具有
的属性equivalent(a, a) == true
和
equivalent(a, b) == equivalent(b, a)
适用于所有a
,b
。
对于T
类型的给定元素集合,我想删除除了每个等价类的第一个出现的所有元素。我想出了下面的代码,但是在徘徊:
是否存在没有显式循环的解决方案?
std::vector<T> filter_all_but_one_for_each_set_of_equivalent_T(std::vector<T> const& ts) {
std::vector<T> result;
for (auto iter = ts.begin(); iter != ts.end(); ++iter) {
auto const& elem = *iter;
bool has_equivalent_element_at_earlier_position = std::any_of(
ts.begin(),
iter,
&equivalent
);
if (not has_equivalent_element_at_earlier_position) {
result.push_back(routing_pin);
}
}
return result;
}
据我所知,std::unique
不会因为我的类型T
无法排序而无法做到。因为在我的案例中我只有C ++ 11,但我也会对教育中的其他选项感兴趣。
答案 0 :(得分:2)
扩展我在AndyG的回答中的评论:
template<class T, class A, class Equivalent>
auto deduplicated2(std::vector<T, A> vec, Equivalent&& equivalent) -> std::vector<T, A>
{
auto current = std::begin(vec);
// current 'last of retained sequence'
auto last = std::end(vec);
while (current != last)
{
// define a predicate which checks for equivalence to current
auto same = [&](T const& x) -> bool
{
return equivalent(*current, x);
};
// move non-equivalent items to end of sequence
// return new 'end of valid sequence'
last = std::remove_if(std::next(current), last, same);
}
// erase all items beyond the 'end of valid sequence'
vec.erase(last, std::end(vec));
return vec;
}
请相信AndyG。
对于T可以清洗的非常大的向量,我们可以针对O(n)解决方案:
template<class T, class A, class Equivalent>
auto deduplicated(std::vector<T, A> const& vec, Equivalent&& equivalent) -> std::vector<T, A>
{
auto seen = std::unordered_set<T, std::hash<T>, Equivalent>(vec.size(), std::hash<T>(), std::forward<Equivalent>(equivalent));
auto result = std::vector<T, A>();
result.resize(vec.size());
auto current = std::begin(vec);
while (current != std::end(vec))
{
if (seen.insert(*current).second)
{
result.push_back(*current);
}
}
return result;
}
最后,重新审视第一个解决方案并重构为子问题(我无法帮助自己):
// in-place de-duplication of sequence, similar interface to remove_if
template<class Iter, class Equivalent>
Iter inplace_deduplicate_sequence(Iter first, Iter last, Equivalent&& equivalent)
{
while (first != last)
{
// define a predicate which checks for equivalence to current
using value_type = typename std::iterator_traits<Iter>::value_type;
auto same = [&](value_type const& x) -> bool
{
return equivalent(*first, x);
};
// move non-equivalent items to end of sequence
// return new 'end of valid sequence'
last = std::remove_if(std::next(first), last, same);
}
return last;
}
// in-place de-duplication on while vector, including container truncation
template<class T, class A, class Equivalent>
void inplace_deduplicate(std::vector<T, A>& vec, Equivalent&& equivalent)
{
vec.erase(inplace_deduplicate_sequence(vec.begin(),
vec.end(),
std::forward<Equivalent>(equivalent)),
vec.end());
}
// non-destructive version
template<class T, class A, class Equivalent>
auto deduplicated2(std::vector<T, A> vec, Equivalent&& equivalent) -> std::vector<T, A>
{
inplace_deduplicate(vec, std::forward<Equivalent>(equivalent));
return vec;
}
答案 1 :(得分:2)
这是一种只有一个非常简单的循环的方式:
首先定义我们的类,我将其称为A
而不是T
,因为T
通常用于模板:
class A{
public:
explicit A(int _i) : i(_i){};
int get() const{return i;}
private:
int i;
};
然后我们的equivalent
函数只比较整数的相等性:
bool equivalent(A const& a, A const& b){return a.get() == b.get();}
接下来我将定义过滤功能。
这里的想法是利用library为我们有效地进行循环和擦除(它通常将元素交换到最后,这样你就不会为每次删除移动向量)。
我们首先删除与第一个元素匹配的所有内容,然后删除与第二个元素匹配的所有内容(现在保证!=第一个元素),依此类推。
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
for(size_t i = 1; i < as.size(); ++i){
as.erase(std::remove_if(as.begin() + i, as.end(), [&as, i](const A& next){return equivalent(as[i-1], next);}), as.end());
}
return as;
}
std::remove
编辑:正如理查德·霍奇斯所说,有可能将任何删除延迟到最后。我不能让它看起来很漂亮:
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
auto end = as.end();
for(size_t i = 1; i < std::distance(as.begin(), end); ++i){
end = std::remove_if(as.begin() + i, end, [&as, i](const A& next){return equivalent(as[i-1], next);});
}
as.erase(end, as.end());
return as;
}
答案 2 :(得分:2)
你可以尝试这个。这里的技巧是在内部谓词中获取索引。
std::vector<T> output;
std::copy_if(
input.begin(), input.end(),
std::back_inserter(output),
[&](const T& x) {
size_t index = &x - &input[0];
return find_if(
input.begin(), input.begin() + index, x,
[&x](const T& y) {
return equivalent(x, y);
}) == input.begin() + index;
});
答案 3 :(得分:1)
首先提出另一个循环版本,与您自己的版本相比,它将统一在一起,您可能会发现它很有趣:
std::vector<int> v({1, 7, 1, 8, 9, 8, 9, 1, 1, 7});
auto retained = v.begin();
for(auto i = v.begin(); i != v.end(); ++i)
{
bool isFirst = true;
for(auto j = v.begin(); j != retained; ++j)
{
if(*i == *j)
{
isFirst = false;
break;
}
}
if(isFirst)
{
*retained++ = *i;
}
}
v.erase(retained, v.end());
这是使用std::remove_if
和std::find_if
的版本的基础:
auto retained = v.begin();
auto c = [&v, &retained](int n)
{
if(std::find_if(v.begin(), retained, [n](int m) { return m == n; }) != retained)
return true;
// element remains, so we need to increase!!!
++retained;
return false;
};
v.erase(std::remove_if(v.begin(), v.end(), c), v.end());
在这种情况下你需要lambda,因为我们需要一个唯一谓词,而等效的(在我的int示例中由operator==
表示)是二进制的...
答案 4 :(得分:1)
由于性能不是问题,您可以使用std::accumulate
扫描元素并将其添加到累加器向量xs
(如果还没有)
xs
中的等价元素。
有了这个,你根本不需要任何手写的原始循环。
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
return std::accumulate(as.begin(), as.end(),
std::vector<A>{}, [](std::vector<A> xs, A const& x) {
if ( std::find_if(xs.begin(), xs.end(), [x](A const& y) {return equivalent(x,y);}) == xs.end() ) {
xs.push_back(x);
}
return xs;
});
}
使用两个辅助函数,这实际上是可读的:
bool contains_equivalent(std::vector<A> const& xs, A const& x) {
return std::find_if(xs.begin(), xs.end(),
[x](A const& y) {return equivalent(x,y);}) != xs.end();
};
std::vector<A> push_back_if(std::vector<A> xs, A const& x) {
if ( !contains_equivalent(xs, x) ) {
xs.push_back(x);
}
return xs;
};
该功能本身只是对std::accumulate
的调用:
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> as) {
return std::accumulate(as.begin(), as.end(), std::vector<A>{}, push_back_if);
}
I've modified AndyG's example code with my proposed function.
如上所述,std::accumulate
使用累加器变量的副本调用push_back_if
,并将返回值再次移动分配给累加器。这是非常低效的,但可以通过更改push_back_if
以获取引用来优化,以便就地修改向量。初始值需要作为参考包装器传递std::ref
以消除剩余的副本。
std::vector<A>& push_back_if(std::vector<A>& xs, A const& x) {
if ( !contains_equivalent(xs, x) ) {
xs.push_back(x);
}
return xs;
};
std::vector<A> filter_all_but_one_for_each_set_of_equivalent_A(std::vector<A> const& as) {
std::vector<A> acc;
return std::accumulate(as.begin(), as.end(), std::ref(acc), push_back_if);
}
You can see in the example that the copy-constructor is almost completely eliminated.
答案 5 :(得分:0)
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 6
at GameLoop$DrawBoard.paintComponent(GameLoop.java:69)
at javax.swing.JComponent.paint(JComponent.java:1056)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
at javax.swing.JComponent.paint(JComponent.java:1042)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
at java.awt.Container.paint(Container.java:1975)
at java.awt.Window.paint(Window.java:3904)
at javax.swing.RepaintManager$4.run(RepaintManager.java:842)
at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
/**
* Author : Matija & Zvonimir
* Version : 1.0
* Purpose : Connect 4 game work. Creating working game with arrays
*
*
*/
import java.util.Arrays;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.JComponent;
import java.awt.Graphics2D;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
public class GameLoop extends JFrame{
// attributes
private int [][] matrix;
int col;
int row;
private boolean gameOver = false;
private int playerTurn = 0;
Scanner scan = new Scanner(System.in);
//constants
private static final int COL = 7;
private static final int ROWS = 6;
public static final int SQUARE_SIZE = 100;
public static final int WIDTH = COL * SQUARE_SIZE;
public static final int HEIGHT = (ROWS+1) * SQUARE_SIZE;
public static final int CIRCLE_WIDTH_HEIGHT = (SQUARE_SIZE) - 15;
//MAIN METHOD
public static void main(String []args){
GameLoop gl = new GameLoop();
//gl.gameOver();
}//main
/*GRAPHICS*/
private class DrawBoard extends JPanel{
int[][] board = new int[ROWS][COL];
public DrawBoard(){
for(int i = 0; i < ROWS; i++){
for(int j = 0; j < COL; j++){
board[i][j] = 0;
}
}//set to 0;
}
public void paintComponent(Graphics g){
super.paintComponents(g); //override
for(int i = 0; i < COL; i++){
for(int j = 0; j < ROWS; j++){
g.setColor(Color.BLUE);
g.fillRect( (i*SQUARE_SIZE),(j * SQUARE_SIZE+SQUARE_SIZE),SQUARE_SIZE,SQUARE_SIZE);
if(board[i][j] == 0){
g.setColor(Color.BLACK);
g.fillOval( (i*SQUARE_SIZE),(j*SQUARE_SIZE +SQUARE_SIZE),CIRCLE_WIDTH_HEIGHT,CIRCLE_WIDTH_HEIGHT);
}else if(board[i][j] == 1){
g.setColor(Color.RED);
g.fillRect( (i*SQUARE_SIZE),(j * SQUARE_SIZE+SQUARE_SIZE),SQUARE_SIZE,SQUARE_SIZE);
}else if(board[i][j] == 2){
g.setColor(Color.YELLOW);
g.fillOval( (i*SQUARE_SIZE),(j*SQUARE_SIZE +SQUARE_SIZE),CIRCLE_WIDTH_HEIGHT,CIRCLE_WIDTH_HEIGHT);
}
}
}
for(int i = 0; i < COL; i++){
for(int j = 0; j < ROWS; j++){
}//for J
} //for I
}//paint component method
}//CLASS
public GameLoop(){
DrawBoard board = new DrawBoard();
add(board);
setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
setPreferredSize( new Dimension(701,732));
//setResizable(false);
setLocation( 400,200 );
setVisible ( true );
pack();
}
/*
* Method that creates a 2d array (matrix) filled with 0s
* @param matrix 2d array
*/
public int[][] createBoard(){
matrix = new int[ROWS][COL];
for(int i = 0; i < ROWS; i++){
for(int j = 0; j < COL; j++){
matrix[i][j] = 0;
}
}//set to 0
return matrix;
}//create board
}//class game loop